<pre class='metadata'>
Title: Web Bluetooth
Repository: WebBluetoothCG/web-bluetooth
Status: CG-DRAFT
ED: https://webbluetoothcg.github.io/web-bluetooth/
Shortname: web-bluetooth
Level: 1
Translation: ja https://tkybpp.github.io/web-bluetooth-jp/
Editor: See contributors on GitHub, , https://github.com/WebBluetoothCG/web-bluetooth/graphs/contributors
Abstract: This document describes an API to discover and communicate with devices
Abstract: over the Bluetooth 4 wireless standard using the Generic Attribute Profile (GATT).

Group: web-bluetooth-cg
!Participate: <a href="https://www.w3.org/community/web-bluetooth/">Join the W3C Community Group</a>
!Participate: <a href="https://github.com/WebBluetoothCG/web-bluetooth">Fix the text through GitHub</a>
!Participate: <a href="mailto:public-web-bluetooth@w3.org">public-web-bluetooth@w3.org</a> (<a href="https://lists.w3.org/Archives/Public/public-web-bluetooth/" rel="discussion">archives</a>)
!Participate: <a href="irc://irc.w3.org:6665/#web-bluetooth">IRC: #web-bluetooth on W3C's IRC</a>

Markup Shorthands: css no, markdown yes
</pre>

<pre class=biblio>
{
  "BLUETOOTH42": {
    "href": "https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439",
    "title": "BLUETOOTH SPECIFICATION Version 4.2",
    "publisher": "Bluetooth SIG",
    "date": "2 December 2014"
  },
  "BLUETOOTH-ASSIGNED": {
    "href": "https://www.bluetooth.org/en-us/specification/assigned-numbers",
    "title": "Assigned Numbers",
    "status": "Living Standard",
    "publisher": "Bluetooth SIG"
  },
  "BLUETOOTH-ASSIGNED-SERVICES": {
    "href": "https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx",
    "title": "Bluetooth GATT Specifications > Services",
    "status": "Living Standard",
    "publisher": "Bluetooth SIG"
  },
  "BLUETOOTH-ASSIGNED-CHARACTERISTICS": {
    "href": "https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx",
    "title": "Bluetooth GATT Specifications > Characteristics",
    "status": "Living Standard",
    "publisher": "Bluetooth SIG"
  },
  "BLUETOOTH-ASSIGNED-DESCRIPTORS": {
    "href": "https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorsHomePage.aspx",
    "title": "Bluetooth GATT Specifications > Descriptors",
    "status": "Living Standard",
    "publisher": "Bluetooth SIG"
  },
  "BLUETOOTH-SUPPLEMENT6": {
    "href": "https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=302735",
    "title": "Supplement to the Bluetooth Core Specification Version 6",
    "date": "14 July 2015",
    "publisher": "Bluetooth SIG"
  }
}
</pre>

<pre class="anchors">
spec: BLUETOOTH-ASSIGNED
    type: enum; urlPrefix: https://developer.bluetooth.org/gatt/
        urlPrefix: characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.
            text: org.bluetooth.characteristic.body_sensor_location; url: body_sensor_location.xml#
            text: org.bluetooth.characteristic.gap.appearance; url: gap.appearance.xml#
            text: org.bluetooth.characteristic.heart_rate_control_point; url: heart_rate_control_point.xml#
            text: org.bluetooth.characteristic.heart_rate_measurement; url: heart_rate_measurement.xml#
            text: org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list; url: ieee_11073-20601_regulatory_certification_data_list.xml#
        urlPrefix: descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.
            text: org.bluetooth.descriptor.gatt.characteristic_presentation_format; url: gatt.characteristic_presentation_format.xml#
            text: org.bluetooth.descriptor.gatt.client_characteristic_configuration; url: gatt.client_characteristic_configuration.xml#
        urlPrefix: services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.
            text: org.bluetooth.service.cycling_power; url: cycling_power.xml#
            text: org.bluetooth.service.heart_rate; url: heart_rate.xml#
    type: dfn
        text: Shortened Local Name; url: https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile#

spec: ECMAScript; urlPrefix: https://tc39.github.io/ecma262/#
    type: abstract-op
        text: CanonicalNumericIndexString; url: sec-canonicalnumericindexstring
        text: CreateDataProperty; url: sec-createdataproperty
        text: IsInteger; url: sec-isinteger
    type: dfn
        text: current realm; url: current-realm
        text: fulfilled; url: sec-promise-objects
        text: internal slot; url: sec-object-internal-methods-and-internal-slots
        text: realm; url: sec-code-realms
    type: method
        text: Array.prototype.map; url: sec-array.prototype.map
        text: [[OwnPropertyKeys]]; for: Object; url: sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys
    type: interface
        text: Array; url: sec-array-objects
        text: ArrayBuffer; url: sec-arraybuffer-constructor
        text: DataView; url: sec-dataview-constructor
        text: Map; url: sec-map-constructor
        text: Promise; url:sec-promise-objects
        text: Set; url: sec-set-objects
        text: TypeError; url: sec-native-error-types-used-in-this-standard-typeerror
        text: TypedArray; url: sec-typedarray-constructors

spec: fingerprinting-guidance; urlPrefix: https://w3c.github.io/fingerprinting-guidance/#
    type: dfn
        text: fingerprinting surface; url: dfn-fingerprinting-surface

spec: WebIDL; urlPrefix: https://heycam.github.io/webidl/#
    type: dfn
        text: a copy of the bytes held; url: dfn-get-buffer-source-copy
</pre>
<pre class="link-defaults">
spec: fingerprinting-guidance
    type: dfn
        text: fingerprinting surface
spec: html
    type: dfn
        text: browsing context; for: /
        text: global object; for: /
</pre>

<style>
  .argument-list { display: inline-block; vertical-align: top; }
  /* Show self-links for various elements. This is incompatible with nearby floats. */
  .note, .why, .example, .issue { overflow: inherit; }

  .unstable::before {
    content: "This section is not stable.";
    float: right;
    color: red;
  }
  .unstable {
    background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='290'><text transform='rotate(-45)' text-anchor='middle' font-family='sans-serif' font-weight='bold' font-size='70' y='210' opacity='.1'>Unstable</text></svg>");
    background-repeat: repeat
  }

 .unstable.example:not(.no-marker)::before {
     content: "Example " counter(example) " (Unstable)";
     float: none;
 }

 .note {
     background-color: rgba(145, 235, 145, .2);
 }
 .example {
     background-color: rgba(240, 230, 170, .2);
 }
 .def {
     background-color: rgba(85, 170, 255, .2);
 }
 .issue {
     background-color: rgba(235, 145, 145, .2);
 }
</style>

<section>
  <h2 id="introduction">Introduction</h2>

  <em>This section is non-normative.</em>

  <p>
    <a href="https://developer.bluetooth.org/">Bluetooth</a> is
    a standard for short-range wireless communication between devices.
    Bluetooth "Classic" (<abbr title="Basic Rate">BR</abbr>/<abbr title="Enhanced Data Rate">EDR</abbr>)
    defines a set of binary protocols and supports speeds up to about 24Mbps.
    Bluetooth 4.0 introduced a new "Low Energy" mode known as "Bluetooth Smart",
    <abbr title="Bluetooth Low Energy">BLE</abbr>, or just <abbr title="Low Energy">LE</abbr>
    which is limited to about 1Mbps but
    allows devices to leave their transmitters off most of the time.
    BLE provides most of its functionality through key/value pairs provided by
    the <a lt="Generic Attribute Profile">Generic Attribute Profile
    (<abbr title="Generic Attribute Profile">GATT</abbr>)</a>.
  </p>

  <p>
    BLE defines multiple roles that devices can play.
    The <a>Broadcaster</a> and <a>Observer</a> roles are
    for transmitter- and receiver-only applications, respectively.
    Devices acting in the <a>Peripheral</a> role can receive connections,
    and devices acting in the <a>Central</a> role can connect to <a>Peripheral</a> devices.
  </p>

  <p>
    A device acting in either the <a>Peripheral</a> or <a>Central</a> role
    can host a <a>GATT Server</a>,
    which exposes a hierarchy of <a>Service</a>s, <a>Characteristic</a>s, and <a>Descriptor</a>s.
    See [[#information-model]] for more details about this hierarchy.
    Despite being designed to support BLE transport,
    the GATT protocol can also run over BR/EDR transport.
  </p>

  <p>
    The first version of this specification allows web pages,
    running on a UA in the <a>Central</a> role, to connect to <a>GATT Server</a>s
    over either a BR/EDR or LE connection.
    While this specification cites the [[BLUETOOTH42]] specification,
    it intends to also support communication
    among devices that only implement Bluetooth 4.0 or 4.1.
  </p>

  <section>
    <h3 id="introduction-examples">Examples</h3>

    <div class="example" id="example-heart-rate-monitor">
      <p>
        To discover and retrieve data from a standard heart rate monitor,
        a website would use code like the following:
      </p>
      <pre highlight="js">
        let chosenHeartRateService = null;

        navigator.bluetooth.<a idl for="Bluetooth" lt="requestDevice()">requestDevice</a>({
          filters: [{
            services: ['heart_rate'],
          }]
        }).then(device => device.gatt.<a for="BluetoothRemoteGATTServer">connect()</a>)
        .then(server => server.<a idl for="BluetoothRemoteGATTServer" lt="getPrimaryService()"
                              >getPrimaryService</a>(<a idl lt="org.bluetooth.service.heart_rate"
            >'heart_rate'</a>))
        .then(service => {
          chosenHeartRateService = service;
          return Promise.all([
            service.<a idl for="BluetoothRemoteGATTService" lt="getCharacteristic()"
                       >getCharacteristic</a>(<a idl lt="org.bluetooth.characteristic.body_sensor_location"
            >'body_sensor_location'</a>)
              .then(handleBodySensorLocationCharacteristic),
            service.<a idl for="BluetoothRemoteGATTService" lt="getCharacteristic()"
                       >getCharacteristic</a>(<a idl lt="org.bluetooth.characteristic.heart_rate_measurement"
            >'heart_rate_measurement'</a>)
              .then(handleHeartRateMeasurementCharacteristic),
          ]);
        });

        function handleBodySensorLocationCharacteristic(characteristic) {
          if (characteristic === null) {
            console.log("Unknown sensor location.");
            return Promise.resolve();
          }
          return characteristic.<a for="BluetoothRemoteGATTCharacteristic">readValue()</a>
          .then(sensorLocationData => {
            let sensorLocation = sensorLocationData.getUint8(0);
            switch (sensorLocation) {
              case 0: return 'Other';
              case 1: return 'Chest';
              case 2: return 'Wrist';
              case 3: return 'Finger';
              case 4: return 'Hand';
              case 5: return 'Ear Lobe';
              case 6: return 'Foot';
              default: return 'Unknown';
            }
          }).then(location => console.log(location));
        }

        function handleHeartRateMeasurementCharacteristic(characteristic) {
          return characteristic.<a for="BluetoothRemoteGATTCharacteristic">startNotifications()</a>
          .then(char => {
            characteristic.addEventListener('<a event>characteristicvaluechanged</a>',
                                            onHeartRateChanged);
          });
        }

        function onHeartRateChanged(event) {
          let characteristic = event.target;
          console.log(parseHeartRate(characteristic.<a attribute for="BluetoothRemoteGATTCharacteristic">value</a>));
        }
      </pre>

      <p>
        <code>parseHeartRate()</code> would be defined using the
        <a idl lt="org.bluetooth.characteristic.heart_rate_measurement"
           ><code>heart_rate_measurement</code> documentation</a>
        to read the {{DataView}} stored
        in a {{BluetoothRemoteGATTCharacteristic}}'s
        {{BluetoothRemoteGATTCharacteristic/value}} field.
      </p>

      <pre highlight="js">
        function parseHeartRate(data) {
          let flags = data.getUint8(0);
          let rate16Bits = flags & 0x1;
          let result = {};
          let index = 1;
          if (rate16Bits) {
            result.heartRate = data.getUint16(index, /*littleEndian=*/true);
            index += 2;
          } else {
            result.heartRate = data.getUint8(index);
            index += 1;
          }
          let contactDetected = flags & 0x2;
          let contactSensorPresent = flags & 0x4;
          if (contactSensorPresent) {
            result.contactDetected = !!contactDetected;
          }
          let energyPresent = flags & 0x8;
          if (energyPresent) {
            result.energyExpended = data.getUint16(index, /*littleEndian=*/true);
            index += 2;
          }
          let rrIntervalPresent = flags & 0x10;
          if (rrIntervalPresent) {
            let rrIntervals = [];
            for (; index + 1 < data.byteLength; index += 2) {
              rrIntervals.push(data.getUint16(index, /*littleEndian=*/true));
            }
            result.rrIntervals = rrIntervals;
          }
          return result;
        }
      </pre>

      <p>
        <code>onHeartRateChanged()</code> might log an object like
      </p>
      <pre highlight="js">
         {
           heartRate: 70,
           contactDetected: true,
           energyExpended: 750,     // Meaning 750kJ.
           rrIntervals: [890, 870]  // Meaning .87s and .85s.
         }
      </pre>

      <p>
        If the heart rate sensor reports the <code>energyExpended</code> field,
        the web application can reset its value to <code>0</code> by writing to the
        {{org.bluetooth.characteristic.heart_rate_control_point|heart_rate_control_point}}
        characteristic:
      </p>

      <pre highlight="js">
        function resetEnergyExpended() {
          if (!chosenHeartRateService) {
            return Promise.reject(new Error('No heart rate sensor selected yet.'));
          }
          return chosenHeartRateService.<a idl for="BluetoothRemoteGATTService" lt="getCharacteristic()"
                                         >getCharacteristic</a>(<a idl lt="org.bluetooth.characteristic.heart_rate_control_point"
            >'heart_rate_control_point'</a>)
          .then(controlPoint => {
            let resetEnergyExpended = new Uint8Array([1]);
            return controlPoint.<a idl for="BluetoothRemoteGATTCharacteristic" lt="writeValue()">writeValue</a>(resetEnergyExpended);
          });
        }
      </pre>
    </div>
  </section>
</section>

<section>
  <h2 id="security-and-privacy">Security and privacy considerations</h2>

  <section>
    <h3 id="device-access-is-powerful">Device access is powerful</h3>

    <p>
      When a website requests access to devices using
      {{Bluetooth/requestDevice()}},
      it gets the ability to access all GATT services mentioned in the call.
      The UA MUST inform the user what capabilities these services give the website
      before asking which devices to entrust to it.
      If any services in the list aren't known to the UA,
      the UA MUST assume they give the site complete control over the device
      and inform the user of this risk.
      The UA MUST also allow the user to inspect what sites have access to what devices
      and <a lt="revoke Bluetooth access">revoke</a> these pairings.

    <p>
      The UA MUST NOT allow the user to pair entire classes of devices with a website.
      It is possible to construct a class of devices
      for which each individual device sends the same Bluetooth-level identifying information.
      UAs are not required to attempt to detect this sort of forgery
      and MAY let a user pair this pseudo-device with a website.

    <p>
      To help ensure that only the entity the user approved for access actually has access,
      this specification requires that only <a>secure context</a>s
      can access Bluetooth devices
      (<a idl lt="Bluetooth">requestDevice</a>).
    </p>
  </section>

  <section class="non-normative">
    <h3 id="server-takeovers">Trusted servers can serve malicious code</h3>

    <em>This section is non-normative.</em>

    <p>
      Even if the user trusts an origin,
      that origin's servers or developers could be compromised,
      or the origin's site could be vulnerable to XSS attacks.
      Either could lead to users granting malicious code access to valuable devices.
      Origins should define a Content Security Policy ([[CSP3]])
      to reduce the risk of XSS attacks,
      but this doesn't help with compromised servers or developers.
    </p>

    <p>
      The ability to retrieve granted devices after a page reload,
      provided by <a href="#permission-api-integration"></a>,
      makes this risk worse.
      Instead of having to get the user to grant access while the site is compromised,
      the attacker can take advantage of previously-granted devices
      if the user simply visits while the site is compromised.
      On the other hand, when sites can keep access to devices across page reloads,
      they don't have to show as many permission prompts overall,
      making it more likely that users will pay attention to the prompts they do see.
    </p>
  </section>

  <section>
    <h3 id="attacks-on-devices">Attacks on devices</h3>

    <em>This section is non-normative.</em>

    <p>
      Communication from websites can break the security model of some devices,
      which assume they only receive messages from
      the trusted operating system of a remote device.
      Human Interface Devices are a prominent example,
      where allowing a website to communicate would allow that site to log keystrokes.
      This specification includes a <a>GATT blocklist</a> of
      such vulnerable services, characteristics, and descriptors
      to prevent websites from taking advantage of them.
    </p>

    <p>
      We expect that many devices are vulnerable to unexpected data delivered to their radio.
      In the past, these devices had to be exploited one-by-one,
      but this API makes it plausible to conduct large-scale attacks.
      This specification takes several approaches to make such attacks more difficult:

    <ul>
      <li>
        Pairing individual devices instead of device classes
        requires at least a user action before a device can be exploited.

      <li>
        Constraining access to <a>GATT</a>, as opposed to generic byte-stream access,
        denies malicious websites access to most parsers on the device.

        <p>
          On the other hand,
          GATT's <a>Characteristic</a> and <a>Descriptor</a> values are still byte arrays,
          which may be set to lengths and formats the device doesn't expect.
          UAs are encouraged to validate these values when they can.

      <li>
        This API never exposes Bluetooth addressing, data signing or encryption keys
        (<a>Definition of Keys and Values</a>) to websites.
        This makes it more difficult for a website to predict the bits that will be sent over the radio,
        which blocks <a href="https://www.usenix.org/legacy/events/woot11/tech/final_files/Goodspeed.pdf">packet-in-packet injection attacks</a>.
        Unfortunately, this only works over encrypted links,
        which not all BLE devices are required to support.
      </li>
    </ul>

    <p>
      UAs can also take further steps to protect their users:

    <ul>
      <li>
        A web service may collect lists of malicious websites and vulnerable devices.
        UAs can deny malicious websites access to any device
        and any website access to vulnerable devices.
    </ul>
  </section>

  <section>
    <h3 id="bluetooth-device-identifiers">Bluetooth device identifiers</h3>

    <em>This section is non-normative.</em>

    <p>
      Each Bluetooth BR/EDR device has a unique 48-bit MAC address
      known as the <a>BD_ADDR</a>.
      Each Bluetooth LE device has at least one of a <a>Public Device Address</a>
      and a <a>Static Device Address</a>.
      The <a>Public Device Address</a> is a MAC address.
      The <a>Static Device Address</a> may be regenerated on each restart.
      A BR/EDR/LE device will use the same value
      for the <a>BD_ADDR</a> and the <a>Public Device Address</a>
      (specified in the <a>Read BD_ADDR Command</a>).
    </p>
    <p>
      An LE device may also have a unique, 128-bit <a>Identity Resolving Key</a>,
      which is sent to trusted devices during the bonding process.
      To avoid leaking a persistent identifier, an LE device may scan and advertise using
      a random Resolvable or Non-Resolvable <a>Private Address</a>
      instead of its Static or Public Address.
      These are regenerated periodically (approximately every 15 minutes),
      but a bonded device can check whether one of its stored <a>IRK</a>s matches
      any given Resolvable Private Address
      using the <a>Resolvable Private Address Resolution Procedure</a>.
    </p>
    <p>
      Each Bluetooth device also has a human-readable <a>Bluetooth Device Name</a>.
      These aren't guaranteed to be unique, but may well be, depending on the device type.

    <section>
      <h4 id="remote-device-identifiers">Identifiers for remote Bluetooth devices</h4>

      <em>This section is non-normative.</em>

      <p>
        If a website can retrieve any of the persistent device IDs,
        these can be used, in combination with a large effort to catalog ambient devices,
        to discover a user's location.
        A device ID can also be used to identify that a user who
        pairs two different websites with the same Bluetooth device
        is a single user.
        On the other hand, many GATT services are available
        that could be used to fingerprint a device,
        and a device can easily expose a custom GATT service to make this easier.
      </p>
      <p>
        This specification <a href="#note-device-id-tracking">suggests</a> that
        the UA use different device IDs for a single device
        when its user doesn't intend scripts to learn that it's a single device,
        which makes it difficult for websites to abuse the device address like this.
        Device makers can still design their devices to help track users,
        but it takes work.
      </p>
    </section>

    <section>
      <h4 id="ua-bluetooth-address">The UA's Bluetooth address</h4>

      <em>This section is non-normative.</em>

      <p>
        In BR/EDR mode, or in LE mode during active scanning without the <a>Privacy Feature</a>,
        the UA broadcasts its persistent ID to any nearby Bluetooth radio.
        This makes it easy to scatter hostile devices in an area and track the UA.
        As of 2014-08, few or no platforms document that they implement the <a>Privacy Feature</a>,
        so despite this spec recommending it, few UAs are likely to use it.
        This spec does <a href="#requestDevice-user-gesture">require a user gesture</a>
        for a website to trigger a scan, which reduces the frequency of scans some,
        but it would still be better for more platforms to expose the <a>Privacy Feature</a>.
    </section>
  </section>

  <section class="non-normative">
    <h3 id="availability-fingerprint">Exposing Bluetooth availability</h3>

    <em>This section is non-normative.</em>

    <p>
      <code>navigator.bluetooth.{{getAvailability()}}</code>
      exposes whether a Bluetooth radio is available on the user's system.
      Some users might consider this private,
      although it's hard to imagine the damage that would result from revealing it.
      This information also increases the UA's <a>fingerprinting surface</a> by a bit.
      This function returns a {{Promise}},
      so UAs have the option of asking the user what value they want to return,
      but we expect the increased risk to be small enough that
      UAs will choose not to prompt.
    </p>
  </section>
</section>

<section>
  <h2 id="device-discovery" oldids="requestDevice-secure-context">Device Discovery</h2>

  <pre class="idl">
    dictionary BluetoothDataFilterInit {
      BufferSource dataPrefix;
      BufferSource mask;
    };
    dictionary BluetoothLEScanFilterInit {
      sequence&lt;BluetoothServiceUUID> services;
      DOMString name;
      DOMString namePrefix;
      // Maps unsigned shorts to BluetoothDataFilters.
      object manufacturerData;
      // Maps BluetoothServiceUUIDs to BluetoothDataFilters.
      object serviceData;
    };

    dictionary RequestDeviceOptions {
      sequence&lt;BluetoothLEScanFilterInit> filters;
      sequence&lt;BluetoothServiceUUID> optionalServices = [];
      boolean acceptAllDevices = false;
    };

    interface Bluetooth : EventTarget {
      [SecureContext]
      Promise&lt;boolean> getAvailability();
      [SecureContext]
      attribute EventHandler onavailabilitychanged;
      [SecureContext, SameObject]
      readonly attribute BluetoothDevice? referringDevice;
      [SecureContext]
      Promise&lt;BluetoothDevice> requestDevice(optional RequestDeviceOptions options);
    };
    Bluetooth implements BluetoothDeviceEventHandlers;
    Bluetooth implements CharacteristicEventHandlers;
    Bluetooth implements ServiceEventHandlers;
  </pre>
  <div class="note" heading="{{Bluetooth}} members">
    <p>
      {{Bluetooth/getAvailability()}} informs the page
      whether Bluetooth is available at all.
      An adapter that's disabled through software should count as available.
      Changes in availability,
      for example when the user physically attaches or detaches an adapter,
      are reported through the {{availabilitychanged}} event.
    </p>
    <p class="unstable">
      {{Bluetooth/referringDevice}} gives access to
      the device from which the user opened this page, if any.
      For example, an <a href="https://developers.google.com/beacons/eddystone">Eddystone</a> beacon
      might advertise a URL, which the UA allows the user to open.
      A {{BluetoothDevice}} representing the beacon would be available
      through <code>navigator.bluetooth.{{referringDevice}}</code>.
    </p>
    <p>
      {{Bluetooth/requestDevice(options)}} asks the user
      to grant this origin access to a device
      that <a>matches any filter</a> in <code>options.<dfn dict-member for="RequestDeviceOptions">filters</dfn></code>.
      To <a>match a filter</a>, the device has to:
    </p>
    <ul>
      <li>
        support <em>all</em> the GATT service UUIDs
        in the <dfn dict-member for="BluetoothLEScanFilterInit">services</dfn> list
        if that member is present,
      </li>
      <li>
        have a name equal to <dfn dict-member for="BluetoothLEScanFilterInit">name</dfn>
        if that member is present,
      </li>
      <li>
        have a name starting with <dfn dict-member for="BluetoothLEScanFilterInit">namePrefix</dfn>
        if that member is present,
      </li>
      <li class="unstable">
        advertise <a>manufacturer specific data</a> matching all of the key/value pairs in
        <dfn dict-member for="BluetoothLEScanFilterInit">manufacturerData</dfn>
        if that member is present, and
      </li>
      <li class="unstable">
        advertise <a>service data</a> matching all of the key/value pairs in
        <dfn dict-member for="BluetoothLEScanFilterInit">serviceData</dfn>
        if that member is present.
      </li>
    </ul>
    <p link-for-hint="BluetoothDataFilterInit" class="unstable">
      Both <a>Manufacturer Specific Data</a> and <a>Service Data</a>
      map a key to an array of bytes.
      {{BluetoothDataFilterInit}} filters these arrays.
      An array matches if it has a |prefix| such that
      <code>|prefix| & {{mask}}</code> is equal to <code>{{dataPrefix}} & {{mask}}</code>.
    </p>
    <p>
      Note that
      if a device changes its behavior significantly when it connects,
      for example by not advertising its identifying manufacturer data anymore
      and instead having the client discover some identifying GATT services,
      the website may need to include filters for both behaviors.
    </p>
    <p>
      In rare cases,
      a device may not advertise enough distinguishing information
      to let a site filter out uninteresting devices.
      In those cases, a site can set
      <dfn dict-member for="RequestDeviceOptions">acceptAllDevices</dfn> to `true`
      and omit all {{RequestDeviceOptions/filters}}.
      This puts the burden of selecting the right device entirely on the site's users.
      If a site uses {{RequestDeviceOptions/acceptAllDevices}},
      it will only be able to use services listed in {{RequestDeviceOptions/optionalServices}}.
    </p>
    <p>
      After the user selects a device to pair with this origin,
      the origin is allowed to access any service whose UUID was listed
      in the {{BluetoothLEScanFilterInit/services}} list
      in any element of <code>options.filters</code>
      or in <code>options.<dfn dict-member for="RequestDeviceOptions">optionalServices</dfn></code>.
    </p>

    <p>
      This implies that if developers filter just by name,
      they must use {{RequestDeviceOptions/optionalServices}} to get access to any services.
    </p>
  </div>

  <div class="example" id="example-filter-by-services">
    <p>
      Say the UA is close to the following devices:
    </p>
    <table class="data">
      <thead><th>Device</th><th>Advertised Services</th></thead>
      <tr><td>D1</td><td>A, B, C, D</td></tr>
      <tr><td>D2</td><td>A, B, E</td></tr>
      <tr><td>D3</td><td>C, D</td></tr>
      <tr><td>D4</td><td>E</td></tr>
      <tr><td>D5</td><td><i>&lt;none></i></td></tr>
    </table>
    <p>
      If the website calls
    </p>
    <pre highlight="js">
      navigator.bluetooth.requestDevice({
        filters: [ {services: [A, B]} ]
      });
    </pre>
    <p>
      the user will be shown a dialog containing devices D1 and D2.
      If the user selects D1, the website will not be able to access services C or D.
      If the user selects D2, the website will not be able to access service E.
    </p>
    <p>
      On the other hand, if the website calls
    </p>
    <pre highlight="js">
      navigator.bluetooth.requestDevice({
        filters: [
          {services: [A, B]},
          {services: [C, D]}
        ]
      });
    </pre>
    <p>
      the dialog will contain devices D1, D2, and D3,
      and if the user selects D1,
      the website will be able to access services A, B, C, and D.
    </p>
    <p>
      The <code>optionalServices</code> list doesn't add any devices
      to the dialog the user sees,
      but it does affect which services the website can use from the device the user picks.
    </p>
    <pre highlight="js">
      navigator.bluetooth.requestDevice({
        filters: [ {services: [A, B]} ],
        optionalServices: \[E]
      });
    </pre>
    <p>
      Shows a dialog containing D1 and D2,
      but not D4, since D4 doesn't contain the required services.
      If the user selects D2, unlike in the first example,
      the website will be able to access services A, B, and E.
    </p>
    <p>
      The allowed services also apply if the device changes after the user grants access.
      For example, if the user selects D1 in the previous <code>requestDevice()</code> call,
      and D1 later adds a new E service,
      that will fire the {{serviceadded}} event,
      and the web page will be able to access service E.
    </p>
  </div>
  <div class="example" id="example-filter-by-name">
    <p>
      Say the devices in the <a href="#example-filter-by-services">previous example</a>
      also advertise names as follows:
    </p>
    <table class="data">
      <thead><th>Device</th><th>Advertised Device Name</th></thead>
      <tr><td>D1</td><td>First De…</td></tr>
      <tr><td>D2</td><td><i>&lt;none></i></td></tr>
      <tr><td>D3</td><td>Device Third</td></tr>
      <tr><td>D4</td><td>Device Fourth</td></tr>
      <tr><td>D5</td><td>Unique Name</td></tr>
    </table>

    <p>
      The following table shows which devices the user can select between
      for several values of <var>filters</var> passed to
      <code>navigator.bluetooth.requestDevice({filters: <var>filters</var>})</code>.
    </p>

    <table class="data">
      <thead><th><var>filters</var></th><th>Devices</th><th>Notes</th></thead>
      <tr>
        <td>
          <pre highlight="js">
            [{name: "Unique Name"}]
          </pre>
        </td>
        <td>D5</td>
        <td></td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{namePrefix: "Device"}]
          </pre>
        </td>
        <td>D3, D4</td>
        <td></td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{name: "First De"},
             {name: "First Device"}]
          </pre>
        </td>
        <td><i>&lt;none></i></td>
        <td>
          D1 only advertises a prefix of its name,
          so trying to match its whole name fails.
        </td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{namePrefix: "First"},
             {name: "Unique Name"}]
          </pre>
        </td>
        <td>D1, D5</td>
        <td></td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{services: \[C],
              namePrefix: "Device"},
             {name: "Unique Name"}]
          </pre>
        </td>
        <td>D3, D5</td>
        <td></td>
      </tr>
    </table>
  </div>

  <div class="example unstable" id="example-filter-by-manufacturer-service-data">
    <p>
      Say the devices in the <a href="#example-filter-by-services">previous example</a>
      also advertise manufacturer or service data as follows:
    </p>
    <table class="data">
      <thead><th>Device</th><th>Manufacturer Data</th><th>Service Data</th></thead>
      <tr><td>D1</td><td>17: 01 02 03</td><td></td></tr>
      <tr><td>D2</td><td></td><td>A: 01 02 03</td></tr>
    </table>

    <p>
      The following table shows which devices the user can select between
      for several values of <var>filters</var> passed to
      <code>navigator.bluetooth.requestDevice({filters: <var>filters</var>})</code>.
    </p>

    <table class="data">
      <thead><th><var>filters</var></th><th>Devices</th></thead>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {17: {}}}]
          </pre>
        </td>
        <td>D1</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{serviceData: {"A": {}}}]
          </pre>
        </td>
        <td>D2</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {17: {}}},
             {serviceData: {"A": {}}}]
          </pre>
        </td>
        <td>D1, D2</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {17: {}},
              serviceData: {"A": {}}}]
          </pre>
        </td>
        <td><i>&lt;none></i></td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {
               17: {dataPrefix: new Uint8Array([1, 2, 3])},
            }}]
          </pre>
        </td>
        <td>D1</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {
               17: {dataPrefix: new Uint8Array([1, 2, 3, 4])},
            }}]
          </pre>
        </td>
        <td><i>&lt;none></i></td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {
               17: {dataPrefix: new Uint8Array([1])},
            }}]
          </pre>
        </td>
        <td>D1</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {
               17: {dataPrefix: new Uint8Array([0x91, 0xAA]),
                    mask: new Uint8Array([0x0F, 0x57])},
            }}]
          </pre>
        </td>
        <td>D1</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            [{manufacturerData: {
               17: {},
               18: {},
            }}]
          </pre>
        </td>
        <td><i>&lt;none></i></td>
      </tr>
    </table>
  </div>

  <div class="example" id="example-disallowed-filters"
       link-for-hint="RequestDeviceOptions">
    <p>
      Filters that either accept or reject all possible devices cause {{TypeError}}s.
      To accept all devices, use {{acceptAllDevices}} instead.
    </p>

    <table class="data">
      <thead><th>Call</th><th>Notes</th></thead>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({})
          </pre>
        </td>
        <td>Invalid: An absent list of filters doesn't accept any devices.</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({filters:[]})
          </pre>
        </td>
        <td>Invalid: An empty list of filters doesn't accept any devices.</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({filters:[{}]})
          </pre>
        </td>
        <td>Invalid: An empty filter accepts all devices, and so isn't allowed either.</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({
              acceptAllDevices:true
            })
          </pre>
        </td>
        <td>
          Valid: Explicitly accept all devices with {{acceptAllDevices}}.
        </td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({
              filters: [...],
              acceptAllDevices:true
            })
          </pre>
        </td>
        <td>Invalid: {{acceptAllDevices}} would override any {{filters}}.</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({
              filters:[{namePrefix: ""}]
            })
          </pre>
        </td>
        <td>Invalid: `namePrefix`, if present, must be non-empty to filter devices.</td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({
              filters:[{manufacturerData: {}}]
            })
          </pre>
        </td>
        <td>
          Invalid: {{BluetoothLEScanFilterInit/manufacturerData}}, if present,
          must be non-empty to filter devices.
        </td>
      </tr>
      <tr>
        <td>
          <pre highlight="js">
            requestDevice({
              filters:[{serviceData: {}}]
            })
          </pre>
        </td>
        <td>
          Invalid: {{BluetoothLEScanFilterInit/serviceData}}, if present,
          must be non-empty to filter devices.
        </td>
      </tr>
    </table>
  </div>

  <p>
    Instances of {{Bluetooth}} are created with the <a>internal slots</a>
    described in the following table:
  </p>
  <table class="data" dfn-for="Bluetooth" dfn-type="attribute">
    <thead>
      <th><a>Internal Slot</a></th>
      <th>Initial Value</th>
      <th>Description (non-normative)</th>
    </thead>
    <tr>
      <td><dfn>\[[deviceInstanceMap]]</dfn></td>
      <td>
        An empty map from <a>Bluetooth device</a>s
        to <code>{{BluetoothDevice}}</code> instances.
      </td>
      <td>
        Ensures only one {{BluetoothDevice}} instance represents each <a>Bluetooth device</a>
        inside a single global object.
      </td>
    </tr>
    <tr>
      <td><dfn>\[[attributeInstanceMap]]</dfn></td>
      <td>
        An empty map from <a>Bluetooth cache</a> entries to {{Promise}}s.
      </td>
      <td>
        The {{Promise}}s resolve to either {{BluetoothRemoteGATTService}},
        {{BluetoothRemoteGATTCharacteristic}}, or {{BluetoothRemoteGATTDescriptor}}
        instances.
      </td>
    </tr>
    <tr>
      <td><dfn>\[[referringDevice]]</dfn></td>
      <td>
        `null`
      </td>
      <td>
        Set to a {{BluetoothDevice}} while <a>initializing a new `Document` object</a>
        if the `Document` was opened from the device.
      </td>
    </tr>
  </table>

  <div class="unstable">
  <p>
    Getting <code>navigator.bluetooth.<dfn attribute for="Bluetooth">referringDevice</dfn></code>
    must return {{[[referringDevice]]}}.
  </p>

  <div algorithm="initializing referringDevice">
    <p>
      Some UAs may allow the user
      to cause a <a>browsing context</a> to <a lt="navigated">navigate</a>
      in response to a <a>Bluetooth device</a>.
      <span class="note">
        For example, if an
        <a href="https://developers.google.com/beacons/eddystone">Eddystone</a> beacon
        advertises a URL,
        the UA may allow the user to navigate to this URL.
      </span>
      If this happens, then as part of <a>initializing a new `Document` object</a>,
      the UA MUST run the following steps:
    </p>
    <ol>
      <li>
        Let <var>referringDevice</var> be the device that caused the navigation.
      </li>
      <li>
        <a>Get the <code>BluetoothDevice</code> representing</a> <var>referringDevice</var>
        inside `navigator.bluetooth`,
        and let |referringDeviceObj| be the result.
      </li>
      <li>
        If the previous step threw an exception, abort these steps.

        Note: This means the UA didn't infer that the user intended to
        grant the current <a>realm</a> access to |referringDevice|.
        For example, the user might have denied GATT access globally.
      </li>
      <li>
        Set <code>navigator.bluetooth.{{[[referringDevice]]}}</code>
        to |referringDeviceObj|.
      </li>
    </ol>
  </div>
  </div>

  <div algorithm>
    <p>
      A <a>Bluetooth device</a> <var>device</var>
      <dfn local-lt="match a filter|matches any filter">matches a filter</dfn> <var>filter</var>
      if the following steps return `match`:
    </p>
    <ol link-for-hint="BluetoothLEScanFilterInit">
      <li>
        If <code><var>filter</var>.{{name}}</code> is present then,
        if <var>device</var>'s <a>Bluetooth Device Name</a> isn't complete
        and equal to <code><var>filter</var>.name</code>,
        return `mismatch`.
      </li>
      <li>
        If <code><var>filter</var>.{{namePrefix}}</code> is present then
        if <var>device</var>'s <a>Bluetooth Device Name</a> isn't present
        or doesn't start with <code><var>filter</var>.namePrefix</code>,
        return `mismatch`.
      </li>
      <li>
        For each <var>uuid</var> in <code><var>filter</var>.{{services}}</code>,
        if the UA has not received advertising data, an <a>extended inquiry response</a>,
        or a service discovery response indicating that
        the device supports a primary (vs included) service with UUID <var>uuid</var>,
        return `mismatch`.
      </li>
      <li>
        If <code><var>filter</var>.{{manufacturerData}}</code> is present then
        for each |manufacturerId| in
        <code>|filter|.manufacturerData.{{Object/[[OwnPropertyKeys]]}}()</code>,
        if <var>device</var> hasn't advertised <a>manufacturer specific data</a>
        with a company identifier code that stringifies in base 10 to |manufacturerId|
        and with data that <a for="BluetoothDataFilterInit">matches</a>
        <code>|filter|.{{manufacturerData}}[|manufacturerId|]</code>
        return `mismatch`.
      </li>
      <li>
        If <code><var>filter</var>.{{serviceData}}</code> is present then
        for each |uuid| in
        <code>|filter|.{{serviceData}}.{{Object/[[OwnPropertyKeys]]}}()</code>,
        if <var>device</var> hasn't advertised <a>service data</a>
        with a UUID whose 128-bit form is |uuid|
        and with data that <a for="BluetoothDataFilterInit">matches</a>
        <code>|filter|.{{serviceData}}[|manufacturerId|]</code>,
        return `mismatch`.
      </li>
      <li>Return `match`.</li>
    </ol>
  </div>

  <div algorithm>
    <p>
      An array of bytes |data|
      <dfn for="BluetoothDataFilterInit" export>matches</dfn> a {{BluetoothDataFilterInit}} |filter|
      if the following steps return `match`.
    </p>
    <p class="note">
      Note: This algorithm assumes that |filter| has already been
      <a for="BluetoothDataFilterInit" lt="canonicalizing">canonicalized</a>.
    </p>
    <ol link-for-hint="BluetoothDataFilterInit">
      <li>
        Let |expectedPrefix| be <a>a copy of the bytes held</a> by <code>|filter|.{{dataPrefix}}</code>.
      </li>
      <li>
        Let |mask| be <a>a copy of the bytes held</a> by <code>|filter|.{{mask}}</code>.
      </li>
      <li>
        If |data| has fewer bytes than |expectedPrefix|, return `mismatch`.
      </li>
      <li>
        For each `1` bit in |mask|,
        if the corresponding bit in |data| is not equal to
        the corresponding bit in |expectedPrefix|,
        return `mismatch`.
      </li>
      <li>
        Return `match`.
      </li>
    </ol>
  </div>

  <p class="note">
    The list of Service UUIDs that a device advertises
    might not include all the UUIDs the device supports.
    The advertising data does specify whether this list is complete.
    If a website filters for a UUID that a nearby device supports but doesn't advertise,
    that device might not be included in the list of devices presented to the user.
    The UA would need to connect to the device to discover the full list of supported services,
    which can impair radio performance and cause delays, so this spec doesn't require it.
  </p>

  <div algorithm>
    <p>
      The <code><dfn method for="Bluetooth">requestDevice(<var>options</var>)</dfn></code> method,
      when invoked, MUST return <a>a new promise</a> |promise|
      and run the following steps <a>in parallel</a>:
    </p>
    <ol link-for-hint="RequestDeviceOptions">
      <li>
        If <code>|options|.{{filters}}</code> is present
        and <code>|options|.{{acceptAllDevices}}</code> is `true`,
        or if <code>|options|.{{filters}}</code> is not present
        and <code>|options|.{{acceptAllDevices}}</code> is `false`,
        <a>reject</a> |promise| with a {{TypeError}} and abort these steps.

        Note: This enforces that
        exactly one of {{filters}} or <code>{{acceptAllDevices}}:true</code> is present.
      </li>
      <li>
        <a>Request Bluetooth devices</a>,
        passing <code>|options|.{{filters}}</code>
        if <code>|options|.{{acceptAllDevices}}</code> is `false`
        or `null` otherwise,
        and passing <code>|options|.{{optionalServices}}</code>,
        and let |devices| be the result.
      </li>
      <li>
        If the previous step threw an exception,
        <a>reject</a> |promise| with that exception and abort these steps.
      </li>
      <li>
        If |devices| is an empty sequence,
        <a>reject</a> |promise| with a {{NotFoundError}} and abort these steps.
      </li>
      <li>
        <a>Resolve</a> |promise| with <code>|devices|[0]</code>.
      </li>
    </ol>
  </div>

  <div algorithm>
    <p>
      To <dfn>request Bluetooth devices</dfn>,
      given a sequence of {{BluetoothLEScanFilterInit}}s, |filters|,
      which can be `null` to represent that all devices can match,
      and a sequence of {{BluetoothServiceUUID}}s, |optionalServices|,
      the UA MUST run the following steps:
    </p>
    <p class="note">
      Note: These steps can block,
      so uses of this algorithm must be <a>in parallel</a>.
    </p>
    <p class="note">
      Calls to this algorithm will eventually be able to request multiple devices,
      but for now it only ever returns a single one.
    </p>
    <ol>
      <li id="requestDevice-user-gesture">
        If the algorithm is not <a>triggered by user activation</a>,
        throw a {{SecurityError}} and abort these steps.
      </li>
      <li>
        In order to convert the arguments from service names and aliases to just <a>UUID</a>s,
        do the following sub-steps:
        <ol>
          <li>
            If <code>|filters| !== null && |filters|.length === 0</code>,
            throw a {{TypeError}}
            and abort these steps.
          </li>
          <li>
            Let <var>uuidFilters</var> be a new {{Array}} and
            <var>requiredServiceUUIDs</var> be a new {{Set}}.
          </li>
          <li>
            If |filters| is `null`,
            then set |requiredServiceUUIDs| to the set of all UUIDs.
          </li>
          <li>
            If |filters| isn't `null`,
            then for each |filter| in |filters|,
            do the following steps:
            <ol>
              <li>
                Let |canonicalFilter| be
                the result of <a for="BluetoothLEScanFilterInit">canonicalizing</a> |filter|.
              </li>
              <li>
                Append |canonicalFilter| to |uuidFilters|.
              </li>
              <li>
                Add the contents of <code>|canonicalFilter|.services</code>
                to |requiredServiceUUIDs|.
              </li>
            </ol>
          </li>
          <li>
            Let <var>optionalServiceUUIDs</var> be
            <code>{{Array.prototype.map}}.call(|optionalServices|,
              {{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>.
          </li>
          <li>
            If any of the {{BluetoothUUID/getService()|BluetoothUUID.getService()}} calls threw an exception,
            throw that exception and abort these steps.
          </li>
          <li>
            Remove from <var>optionalServiceUUIDs</var> any UUIDs that are <a>blocklisted</a>.
          </li>
        </ol>
      </li>
      <li>
        Let |descriptor| be
        <pre highlight="js">
        {
          name: "bluetooth",
          filters: <var>uuidFilters</var>,
          optionalServices: <var>optionalServiceUUIDs</var>,
          acceptAllDevices: <var>filters</var> !== null,
        }
        </pre>
      </li>
      <li>
        Let |state| be |descriptor|'s <a>permission state</a>.

        Note: |state| will be {{"denied"}} in <a>non-secure contexts</a> because
        {{"bluetooth"}} doesn't set the <a>allowed in non-secure contexts</a> flag.
      </li>
      <li>
        If |state| is {{"denied"}}, return `[]` and abort these steps.
      </li>
      <li>
        If the UA can prove that no devices could possibly be found in the next step,
        for example because there is no Bluetooth adapter with which to scan,
        or because the filters can't be matched by any possible advertising packet,
        the UA MAY return `[]` and abort these steps.
      </li>
      <li>
        <a>Scan for devices</a> with
        <var>requiredServiceUUIDs</var>
        as the <i>set of <a>Service</a> UUIDs</i>,
        and let <var>scanResult</var> be the result.
      </li>
      <li>
        If |filters| isn't null,
        remove devices from <var>scanResult</var> if
        they do not <a>match a filter</a>
        in <var>uuidFilters</var>.
      </li>
      <li id="requestDevice-prompt">
        Even if <var>scanResult</var> is empty,
        <a>prompt the user to choose</a> one of the devices in |scanResult|,
        associated with |descriptor|,
        and let |device| be the result.

        <p>
          The UA MAY allow the user to select a nearby device
          that does not match <var>uuidFilters</var>.
        </p>

        <p class="note">
          The UA should show the user the human-readable name of each device.
          If this name is not available because, for example,
          the UA's Bluetooth system doesn't support privacy-enabled scans,
          the UA should allow the user to indicate interest and
          then perform a privacy-disabled scan to retrieve the name.
        </p>
      </li>
      <li>
        If |device| is {{"denied"}}, return `[]` and abort these steps.
      </li>
      <li class="note">
        Choosing a |device| probably indicates that
        the user intends that device to appear in
        the {{BluetoothPermissionData/allowedDevices}} list
        of {{"bluetooth"}}'s <a>extra permission data</a> for
        at least the <a>current settings object</a>,
        for its {{AllowedBluetoothDevice/mayUseGATT}} field to be `true`,
        and for all the services in the union of <var>requiredServiceUUIDs</var>
        and <var>optionalServiceUUIDs</var>
        to appear in its {{AllowedBluetoothDevice/allowedServices}} list,
        in addition to any services that were already there.
      </li>
      <li>
        The UA MAY <a>populate the Bluetooth cache</a> with
        all Services inside <var>device</var>.
        Ignore any errors from this step.
      </li>
      <li>
        <a>Get the <code>BluetoothDevice</code> representing</a> <var>device</var>
        inside the <a>context object</a>,
        propagating any exception,
        and let |deviceObj| be the result.
      </li>
      <li>
        Return <code>[|deviceObj|]</code>.
      </li>
    </ol>
  </div>

  <div algorithm="canonicalizing an LE scan filter">
    <p>
      The result of <dfn for="BluetoothLEScanFilterInit" export>canonicalizing</dfn>
      the {{BluetoothLEScanFilterInit}} |filter|,
      is the {{BluetoothLEScanFilterInit}} returned from the following steps:
    </p>
    <ol>
      <li>
        If none of <var>filter</var>'s
        members is present,
        throw a {{TypeError}}
        and abort these steps.
      </li>
      <li>Let <var>canonicalizedFilter</var> be `{}`.</li>
      <li>
        If <code><var>filter</var>.{{BluetoothLEScanFilterInit/services}}</code> is present,
        do the following sub-steps:
        <ol>
          <li>
            If <code><var>filter</var>.services.length === 0</code>,
            throw a {{TypeError}}
            and abort these steps.
          </li>
          <li>
            Let <var>services</var> be
            <code>{{Array.prototype.map}}.call(<var>filter</var>.services,
                    {{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>.
          </li>
          <li>
            If any of the {{BluetoothUUID/getService()|BluetoothUUID.getService()}} calls
            threw an exception,
            throw that exception and abort these steps.
          </li>
          <li>
            If any service in <var>services</var> is <a>blocklisted</a>,
            throw a {{SecurityError}}
            and abort these steps.
          </li>
          <li>
            Set <code><var>canonicalizedFilter</var>.services</code> to <var>services</var>.
          </li>
        </ol>
      </li>
      <li>
        If <code><var>filter</var>.{{BluetoothLEScanFilterInit/name}}</code> is present,
        do the following sub-steps.
        <ol>
          <li>
            If the <a lt="utf-8 encode">UTF-8 encoding</a>
            of <code><var>filter</var>.name</code>
            is more than 248 bytes long,
            throw a {{TypeError}}
            and abort these steps.

            <p class="note">
              248 is the maximum number of UTF-8 code units in
              a <a>Bluetooth Device Name</a>.
            </p>
          </li>
          <li>
            Set <code><var>canonicalizedFilter</var>.name</code>
            to <code><var>filter</var>.name</code>.
          </li>
        </ol>
      </li>
      <li>
        If <code><var>filter</var>.{{BluetoothLEScanFilterInit/namePrefix}}</code> is present,
        do the following sub-steps.
        <ol>
          <li>
            If <code><var>filter</var>.namePrefix.length === 0</code>
            or if the <a lt="utf-8 encode">UTF-8 encoding</a>
            of <code><var>filter</var>.namePrefix</code>
            is more than 248 bytes long,
            throw a {{TypeError}}
            and abort these steps.

            <p class="note">
              248 is the maximum number of UTF-8 code units in
              a <a>Bluetooth Device Name</a>.
            </p>
          </li>
          <li>
            Set <code><var>canonicalizedFilter</var>.namePrefix</code>
            to <code><var>filter</var>.namePrefix</code>.
          </li>
        </ol>
      </li>

      <li>
        Set <code>|canonicalizedFilter|.manufacturerData</code> to `{}`.
      </li>
      <li>
        If <code>|filter|.{{BluetoothLEScanFilterInit/manufacturerData}}</code> is present,
        do the following sub-steps for each |key| in
        <code>|filter|.manufacturerData.{{Object/[[OwnPropertyKeys]]}}()</code>.
        If there are no such keys, throw a {{TypeError}} and abort these steps.
        <ol>
          <li>
            Let |manufacturerId| be <a abstract-op>CanonicalNumericIndexString</a>(|key|).
          </li>
          <li>
            If |manufacturerId| is `undefined` or `-0`,
            or <a abstract-op>IsInteger</a>(|manufacturerId|) is `false`,
            or |manufacturerId| is outside the range from 0–65535 inclusive,
            throw a {{TypeError}} and abort these steps.
          </li>
          <li>
            Let |dataFilter| be
            <code>|filter|.manufacturerData[|manufacturerId|]</code>,
            <a>converted to an IDL value</a> of type {{BluetoothDataFilterInit}}.
            If this conversion throws an exception, propagate it and abort these steps.
          </li>
          <li>
            Let |canonicalizedDataFilter| be
            the result of <a for="BluetoothDataFilterInit">canonicalizing</a> |dataFilter|,
            <a>converted to an ECMAScript value</a>.
            If this throws an exception, propagate that exception and abort these steps.
          </li>
          <li>
            Call <a abstract-op>CreateDataProperty</a>(|canonicalizedFilter|.manufacturerData,
            |key|, |canonicalizedDataFilter|).
          </li>
        </ol>
      </li>

      <li>
        Set <code>|canonicalizedFilter|.serviceData</code> to `{}`.
      </li>
      <li>
        If <code>|filter|.{{BluetoothLEScanFilterInit/serviceData}}</code> is present,
        do the following sub-steps for each |key| in
        <code>|filter|.serviceData.{{Object/[[OwnPropertyKeys]]}}()</code>.
        If there are no such keys, throw a {{TypeError}} and abort these steps.
        <ol>
          <li>
            Let |serviceName| be <a abstract-op>CanonicalNumericIndexString</a>(|key|).
          </li>
          <li>
            If |serviceName| is `undefined`, set |serviceName| to |key|.
          </li>
          <li>
            Let |service| be
            <code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}(|serviceName|)</code>.
          </li>
          <li>
            If the previous step threw an exception,
            throw that exception and abort these steps.
          </li>
          <li>
            If |service| is <a>blocklisted</a>,
            throw a {{SecurityError}}
            and abort these steps.
          </li>
          <li>
            Let |dataFilter| be
            <code>|filter|.serviceData[|service|]</code>,
            <a>converted to an IDL value</a> of type {{BluetoothDataFilterInit}}.
            If this conversion throws an exception, propagate it and abort these steps.
          </li>
          <li>
            Let |canonicalizedDataFilter| be
            the result of <a for="BluetoothDataFilterInit">canonicalizing</a> |dataFilter|,
            <a>converted to an ECMAScript value</a>.
            If this throws an exception, propagate that exception and abort these steps.
          </li>
          <li>
            Call <a abstract-op>CreateDataProperty</a>(|canonicalizedFilter|.serviceData,
            |service|, |canonicalizedDataFilter|).
          </li>
        </ol>
      </li>

      <li>Return |canonicalizedFilter|.</li>
    </ol>
  </div>

  <div algorithm="canonicalizing a data filter">
    <p>
      The result of <dfn for="BluetoothDataFilterInit" export>canonicalizing</dfn>
      the {{BluetoothDataFilterInit}} |filter|,
      is the {{BluetoothDataFilterInit}} returned from the following steps:
    </p>
    <ol>
      <li>
        If <code>|filter|.{{BluetoothDataFilterInit/dataPrefix}}</code> is present,
        let |dataPrefix| be <a>a copy of the bytes held</a>
        by <code>|filter|.dataPrefix</code>.
        Otherwise, let |dataPrefix| be an empty sequence of bytes.
      </li>
      <li>
        If <code>|filter|.{{BluetoothDataFilterInit/mask}}</code> is present,
        let |mask| be <a>a copy of the bytes held</a>
        by <code>|filter|.mask</code>.
        Otherwise, let |mask| be a sequence of `0xFF` bytes the same length as |dataPrefix|.
      </li>
      <li>
        If |mask| is not the same length as |dataPrefix|,
        throw a {{TypeError}} and abort these steps.
      </li>
      <li>
        Return `{dataPrefix: new Uint8Array(|dataPrefix|), mask: new Uint8Array(|mask|)}`.
      </li>
    </ol>
  </div>

  <div algorithm>
    <p>
      To <dfn>scan for devices</dfn> with
      an optional <var>set of <a>Service</a> UUIDs</var>, defaulting to the set of all UUIDs,
      the UA MUST perform the following steps:
    </p>
    <ol>
      <li>
        If the UA has scanned for devices recently
        <span class="issue">TODO: Nail down the amount of time.</span>
        with a set of UUIDs that was a superset of the UUIDs for the current scan,
        then the UA MAY return the result of that scan and abort these steps.
      </li>
      <li>
        Let <var>nearbyDevices</var> be a set of <a>Bluetooth device</a>s,
        initially equal to the set of devices that
        are connected (have an <a>ATT Bearer</a>) to the UA.
      </li>
      <li>
        If the UA supports the LE transport, perform the <a>General Discovery Procedure</a>,
        except that the UA may include devices that have no <a>Discoverable Mode</a> flag set,
        and add the discovered <a>Bluetooth device</a>s to <var>nearbyDevices</var>.
        The UA SHOULD enable the <a>Privacy Feature</a>.

        <p class="issue">
          Both <a>passive scanning</a> and
          the <a>Privacy Feature</a> avoid leaking the unique, immutable device ID.
          We ought to require UAs to use either one,
          but none of the OS APIs appear to expose either.
          Bluetooth also makes it hard to use <a>passive scanning</a> since
          it doesn't require <a>Central</a> devices to support the
          <a>Observation Procedure</a>.
        </p>
      </li>
      <li>
        If the UA supports the BR/EDR transport, perform the <a>Device Discovery Procedure</a>
        and add the discovered <a>Bluetooth device</a>s to <var>nearbyDevices</var>.

        <p class="issue">
          All forms of BR/EDR inquiry/discovery
          appear to leak the unique, immutable device address.
        </p>
      </li>
      <li>Let <var>result</var> be a set of <a>Bluetooth device</a>s, initially empty.</li>
      <li>
        For each <a>Bluetooth device</a> <var>device</var> in <var>nearbyDevices</var>,
        do the following sub-steps:
        <ol>
          <li>
            If <var>device</var>'s <a>supported physical transports</a> include LE and
            its <a>Bluetooth Device Name</a> is partial or absent,
            the UA SHOULD perform the <a>Name Discovery Procedure</a>
            to acquire a complete name.
          </li>
          <li>
            If <var>device</var>'s advertised <a>Service UUIDs</a>
            have a non-empty intersection with the <var>set of <a>Service</a> UUIDs</var>,
            add <var>device</var> to <var>result</var> and abort these sub-steps.

            <p class="note">
              For BR/EDR devices, there is no way to distinguish GATT from non-GATT services
              in the <a>Extended Inquiry Response</a>.
              If a site filters to the UUID of a non-GATT service,
              the user may be able to select a device
              for the result of <code>requestDevice</code>
              that this API provides no way to interact with.
            </p>
          </li>
          <li>
            The UA MAY connect to <var>device</var> and <a>populate the Bluetooth cache</a>
            with all Services whose UUIDs are in the <var>set of <a>Service</a> UUIDs</var>.
            If <var>device</var>'s <a>supported physical transports</a> include BR/EDR,
            then in addition to the standard GATT procedures,
            the UA MAY use the Service Discovery Protocol (<a>Searching for Services</a>)
            when populating the cache.

            <div class="note" id="note-extra-discovery">
              <p>
                Connecting to every nearby device to discover services costs power and
                can slow down other use of the Bluetooth radio.
                UAs should only discover extra services on a device if
                they have some reason to expect that device to be interesting.
              </p>

              <p>
                UAs should also help developers avoid relying on this extra discovery behavior.
                For example, say a developer has previously connected to a device,
                so the UA knows the device's full set of supported services.
                If this developer then filters using a non-advertised UUID,
                the dialog they see may include this device,
                even if the filter would likely exclude the device on users' machines.
                The UA could provide a developer option to warn when this happens or
                to include only advertised services in matching filters.
              </p>
            </div>
          </li>
          <li>
            If the <a>Bluetooth cache</a> contains
            known-present Services inside <var>device</var>
            with UUIDs in the <var>set of <a>Service</a> UUIDs</var>,
            the UA MAY add <var>device</var> to <var>result</var>.
          </li>
        </ol>
      </li>
      <li>Return <var>result</var> from the scan.</li>
    </ol>
  </div>

  <p class="issue">
    We need a way for a site to register to receive an event
    when an interesting device comes within range.
  </p>

  <section class="unstable">
    <h3 id="permission-api-integration">Permission API Integration</h3>

    <p>
      The [[permissions]] API provides a uniform way
      for websites to request permissions from users
      and query which permissions they have.
    </p>

    <div class="example" id="example-permission-api-request">
      <p>
        Sites can use
        <code highlight="js">navigator.permissions.{{Permissions/request()|request}}({<a idl for="PermissionDescriptor">name</a>: "bluetooth", ...})</code>
        as an alternate spelling of
        <code highlight="js">navigator.bluetooth.{{Bluetooth/requestDevice()}}</code>.
      </p>
      <pre highlight="js">
        navigator.permissions.<a idl for="Permissions" lt="request()">request</a>({
          name: "bluetooth",
          filters: [{
            services: ['heart_rate'],
          }]
        }).then(result => {
          if (result.<a idl for="BluetoothPermissionResult">devices</a>.length > 1) {
            return result.devices[0];
          } else {
            throw new <a idl>DOMException</a>("Chooser cancelled", "NotFoundError");
          }
        }).then(device => {
          sessionStorage.lastDevice = device.<a idl for="BluetoothDevice">id</a>;
        });
      </pre>
      <p>
        The {{BluetoothPermissionDescriptor/deviceId}} member
        is ignored in calls to {{Permissions/request()}}.
      </p>
    </div>

    <div class="example" id="example-permission-api-query">
      <p>
        Once a site has been granted access to a set of devices,
        it can use
        <code highlight="js">navigator.permissions.{{Permissions/query()|query}}({<a idl for="PermissionDescriptor">name</a>: "bluetooth", ...})</code>
        to retrieve those devices after a reload.
      </p>
      <pre highlight="js">
        navigator.permissions.<a idl for="Permissions" lt="query()">query</a>({
          name: "bluetooth",
          deviceId: sessionStorage.lastDevice,
        }).then(result => {
          if (result.<a idl for="BluetoothPermissionResult">devices</a>.length == 1) {
            return result.devices[0];
          } else {
            throw new <a idl>DOMException</a>("Lost permission", "NotFoundError");
          }
        }).then(...);
      </pre>
    </div>

    <p>
      The <dfn enum-value for="PermissionName">"bluetooth"</dfn> <a>powerful feature</a>'s
      permission-related algorithms and types
      are defined as follows:
    </p>
    <dl>
      <dt>
        <a>permission descriptor type</a>
      </dt>
      <dd>
        <pre class="idl">
          dictionary BluetoothPermissionDescriptor : PermissionDescriptor {
            DOMString deviceId;
            // These match RequestDeviceOptions.
            sequence&lt;BluetoothLEScanFilterInit> filters;
            sequence&lt;BluetoothServiceUUID> optionalServices = [];
            boolean acceptAllDevices = false;
          };
        </pre>
      </dd>
      <dt>
        <a>extra permission data type</a>
      </dt>
      <dd>
        <p oldids="allowed-devices-map,device-id,allowed-services-list"
        >{{BluetoothPermissionData}}, defined as:</p>
        <pre class="idl">
          dictionary AllowedBluetoothDevice {
            required DOMString deviceId;
            required boolean mayUseGATT;
            // An allowedServices of "all" means all services are allowed.
            required (DOMString or sequence&lt;UUID>) allowedServices;
          };
          dictionary BluetoothPermissionData {
            required sequence&lt;AllowedBluetoothDevice> allowedDevices = [];
          };
        </pre>
        <p>
          {{AllowedBluetoothDevice}} instances have an <a>internal slot</a>
          <dfn attribute for="AllowedBluetoothDevice">\[[device]]</dfn>
          that holds a <a>Bluetooth device</a>.
        </p>
      </dd>

      <dt>
        <a>extra permission data constraints</a>
      </dt>
      <dd>
        <p>
          Distinct elements of {{BluetoothPermissionData/allowedDevices}}
          must have different {{AllowedBluetoothDevice/[[device]]}}s
          and different {{AllowedBluetoothDevice/deviceId}}s.
        </p>
        <p>
          If {{AllowedBluetoothDevice/mayUseGATT}} is `false`,
          {{AllowedBluetoothDevice/allowedServices}} must be `[]`.
        </p>
        <div class="note" id="note-device-id-tracking">
          <p>
            A {{AllowedBluetoothDevice/deviceId}} allows a site to track that
            a {{BluetoothDevice}} instance seen at one time represents
            the same device as another {{BluetoothDevice}} instance seen at another time,
            possibly in a different <a>realm</a>.
            UAs should consider whether their user intends that tracking to happen or not-happen
            when returning {{"bluetooth"}}'s <a>extra permission data</a>.
          </p>
          <p>
            For example, users generally don't intend two different origins to know
            that they're interacting with the same device,
            and they generally don't intend unique identifiers to persist
            after they've cleared an origin's cookies.
          </p>
        </div>
      </dd>
      <dt>
        <a>permission result type</a>
      </dt>
      <dd>
        <pre class="idl">
          interface BluetoothPermissionResult : PermissionStatus {
            attribute FrozenArray&lt;BluetoothDevice> devices;
          };
        </pre>
      </dd>
      <dt>
        <a>permission request algorithm</a>
      </dt>
      <dd algorithm="permission request">
        <p>
          To <dfn>request the "bluetooth" permission</dfn> with
          a {{BluetoothPermissionDescriptor}} <var>options</var>
          and a {{BluetoothPermissionResult}} <var>status</var>,
          the UA MUST run the following steps:
        </p>
        <ol link-for-hint="BluetoothPermissionDescriptor">
          <li>
            If <code>|options|.{{filters}}</code> is present
            and <code>|options|.{{acceptAllDevices}}</code> is `true`,
            or if <code>|options|.{{filters}}</code> is not present
            and <code>|options|.{{acceptAllDevices}}</code> is `false`,
            throw a {{TypeError}}.

            Note: This enforces that
            exactly one of {{filters}} or <code>{{acceptAllDevices}}:true</code> is present.
          </li>
          <li>
            <a>Request Bluetooth devices</a>,
            passing <code>|options|.{{filters}}</code>
            if <code>|options|.{{acceptAllDevices}}</code> is `false`
            or `null` otherwise,
            and passing <code>|options|.{{optionalServices}}</code>,
            propagating any exception,
            and let |devices| be the result.
          </li>
          <li>
            Set <code>|status|.devices</code> to
            a new {{FrozenArray}} whose contents are the elements of |devices|.
          </li>
        </ol>
      </dd>

      <dt>
        <a>permission query algorithm</a>
      </dt>
      <dd algorithm>
        To <dfn>query the "bluetooth" permission</dfn> with
        a {{BluetoothPermissionDescriptor}} <var>desc</var>
        and a {{BluetoothPermissionResult}} <var>status</var>, the UA must:
        <ol>
          <li>
            Let <var>global</var> be the <a>relevant global object</a> for <var>status</var>.
          </li>
          <li>
            Set <code><var>status</var>.{{PermissionStatus/state}}</code>
            to |desc|'s <a>permission state</a>.
          </li>
          <li>
            If <code>|status|.{{PermissionStatus/state}}</code> is {{"denied"}},
            set <code>|status|.devices</code> to an empty {{FrozenArray}}
            and abort these steps.
          </li>
          <li>
            Let <var>matchingDevices</var> be a new {{Array}}.
          </li>
          <li>
            Let |data|, a {{BluetoothPermissionData}},
            be {{"bluetooth"}}'s <a>extra permission data</a>
            for the <a>current settings object</a>.
          </li>
          <li>
            For each |allowedDevice| in <code>|data|.allowedDevices</code>,
            run the following sub-steps:
            <ol>
              <li>
                If <code><var>desc</var>.deviceId</code> is set
                and <code><var>allowedDevice</var>.deviceId != <var>desc</var>.deviceId</code>,
                continue to the next <var>allowedDevice</var>.
              </li>
              <li>
                If <code><var>desc</var>.filters</code> is set, do the following sub-steps:
                <ol>
                  <li>
                    Replace each filter in <code>|desc|.filters</code>
                    with the result of <a for="BluetoothLEScanFilterInit">canonicalizing</a> it.
                    If any of these canonicalizations throws an error, return that error and abort these steps.
                  </li>
                  <li>
                    If <code><var>allowedDevice</var>.{{[[device]]}}</code> does not
                    <a>match a filter</a> in <code>|desc|.filters</code>,
                    continue to the next <var>allowedDevice</var>.
                  </li>
                </ol>
              </li>
              <li>
                <a>Get the `BluetoothDevice` representing</a>
                <code><var>allowedDevice</var>.{{[[device]]}}</code>
                within <code><var>global</var>.navigator.bluetooth</code>,
                and add the result to <var>matchingDevices</var>.
              </li>
            </ol>
            <p class="note">
              Note: The <code>|desc|.optionalServices</code> field does not affect the result.
            </p>
          </li>
          <li>
            Set <code><var>status</var>.devices</code> to
            a new {{FrozenArray}} whose contents are <var>matchingDevices</var>.
          </li>
        </ol>
      </dd>

      <dt>
        <a>permission revocation algorithm</a>
      </dt>
      <dd algorithm>
        <p>
          To <dfn>revoke Bluetooth access</dfn> to devices the user no longer intends to expose,
          the UA MUST run the following steps:
        </p>
        <ol>
          <li>
            Let |data|, a {{BluetoothPermissionData}},
            be {{"bluetooth"}}'s <a>extra permission data</a>
            for the <a>current settings object</a>.
          </li>
          <li>
            For each {{BluetoothDevice}} instance |deviceObj| in the <a>current realm</a>,
            run the following sub-steps:
            <ol>
              <li>
                If there is an {{AllowedBluetoothDevice}} |allowedDevice| in <code>|data|.{{allowedDevices}}</code>
                such that:
                <ul>
                  <li>
                    <code>|allowedDevice|.{{[[device]]}}</code> is
                    the <a>same device</a> as <code>|deviceObj|.{{[[representedDevice]]}}</code>, and
                  </li>
                  <li>
                    <code>|allowedDevice|.{{AllowedBluetoothDevice/deviceId}} ===
                    |deviceObj|.{{BluetoothDevice/id}}</code>,
                  </li>
                </ul>
                then update <code>|deviceObj|.{{[[allowedServices]]}}</code> to be
                <code>|allowedDevice|.{{allowedServices}}</code>,
                and continue to the next |deviceObj|.
              </li>
              <li>
                Otherwise, detach |deviceObj| from its device by running the remaining steps.
              </li>
              <li>
                Call <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/disconnect()}}</code>.

                Note: This fires a {{gattserverdisconnected}} event at |deviceObj|.
              </li>
              <li>
                Set <code><var>deviceObj</var>.{{[[representedDevice]]}}</code> to `null`.
              </li>
            </ol>
          </li>
        </ol>
      </dd>
    </dl>
  </section>

  <section class="unstable">
    <h3 id="availability">Overall Bluetooth availability</h3>

    <p>
      The UA may be running on a computer that has no Bluetooth radio.
      {{requestDevice()}} handles this by failing to discover any devices,
      which results in a {{NotFoundError}},
      but websites may be able to handle it more gracefully.
    </p>

    <div class="example" id="example-getavailability">
      <p>
        To show Bluetooth UI only to users who have a Bluetooth adapter:
      </p>
      <pre highlight="js">
      let bluetoothUI = document.querySelector('#bluetoothUI');
      navigator.bluetooth.<a idl>getAvailability()</a>.then(isAvailable => {
        bluetoothUI.hidden = !isAvailable;
      });
      navigator.bluetooth.addEventListener('<a idl>availabilitychanged</a>', e => {
        bluetoothUI.hidden = !e.value;
      });
      </pre>
    </div>

    <div algorithm>
      <p>
        The <code><dfn method for="Bluetooth">getAvailability()</dfn></code> method,
        when invoked, MUST return <a>a new promise</a> |promise|
        and run the following steps <a>in parallel</a>:
      </p>
      <ol>
        <li id="override-availability">
          If the user has configured the UA to
          return a particular answer from this function for the current origin,
          <a>queue a task</a> to <a>resolve</a> |promise| with the configured answer,
          and abort these steps.
        </li>
        <li>
          If the UA has the ability to use Bluetooth,
          <a>queue a task</a> to <a>resolve</a> |promise| with `true`.
        </li>
        <li>
          Otherwise, <a>queue a task</a> to <a>resolve</a> |promise| with `false`.
        </li>
      </ol>

      Note: the promise is resolved <a>in parallel</a>
      to let the UA call out to other systems
      to determine whether Bluetooth is available.
    </div>

    <div algorithm="availabilitychanged" id="availability-changed-algorithm">
      <p>
        If the UA becomes able or unable to use Bluetooth,
        for example because a radio was physically attached or detached,
        or the user has changed
        their <a href="#override-availability">configuration</a> for
        the answer returned from {{getAvailability()}},
        the UA must <a>queue a task</a> on
        each <a>global object</a> |global|'s <a>responsible event loop</a>
        to run the following steps:
      </p>
      <ol>
        <li>
          Let |oldAvailability| be
          the value {{getAvailability()}} would have returned before the change.
        </li>
        <li>
          Let |newAvailability| be
          the value {{getAvailability()}} would return after the change.
        </li>
        <li>
          If |oldAvailability| is not the same as |newAvailability|,
          <a>fire an event</a> named {{availabilitychanged}}
          using the {{ValueEvent}} interface
          at <code>|global|.navigator.bluetooth</code>
          with its {{ValueEvent/value}} attribute initialized to |newAvailability|.
        </li>
      </ol>
    </div>

    <pre class="idl">
      [Constructor(DOMString type, optional ValueEventInit initDict)]
      interface ValueEvent : Event {
        readonly attribute any value;
      };

      dictionary ValueEventInit : EventInit {
        any value = null;
      };
    </pre>

    <p>
      {{ValueEvent}} instances are constructed
      as specified in [[DOM#constructing-events]].
    </p>

    <p>
      The <dfn attribute for="ValueEvent">value</dfn> attribute
      must return the value it was initialized to.
    </p>

    <p class="issue" id="ValueEvent-too-generic">
      Such a generic event type belongs in [[HTML]] or [[DOM]], not here.
    </p>
  </section>
</section>

<section>
  <h2 id="device-representation">Device Representation</h2>

  <p>
    The UA needs to track Bluetooth device properties at several levels:
    globally, per origin, and per <a>global object</a>.
  </p>

  <section>
    <h3 id="global-device-properties">Global Bluetooth device properties</h3>

    <p>
      The physical Bluetooth device may be guaranteed to have
      some properties that the UA may not have received.
      Those properties are described as optional here.
    </p>

    <p>
      A <dfn export>Bluetooth device</dfn> has the following properties.
      Optional properties are not present, and sequence and map properties are empty,
      unless/until described otherwise.
      Other properties have a default specified or are specified when a device is introduced.
    </p>

    <ul>
      <li>
        A set of <dfn>supported physical transports</dfn>,
        including one or both of BR/EDR and LE.
        This set will generally be filled based on
        the transports over which the device was discovered
        and the <a>Flags Data Type</a>
        in the <a>Advertising Data</a> or <a>Extended Inquiry Response</a>.
      </li>
      <li>
        One or more of several kinds of 48-bit address:
        a <a>Public Bluetooth Address</a>, a (random) <a>Static Address</a>,
        and a resolvable or non-resolvable <a>Private Address</a>.
      </li>
      <li>An optional 128-bit <a>Identity Resolving Key</a>.</li>

      <li>
        An optional partial or complete <a>Bluetooth Device Name</a>.
        A device has a partial name when the <a>Shortened Local Name</a> AD data was received,
        but the full name hasn't been read yet.
        The <a>Bluetooth Device Name</a> is encoded as UTF-8 and
        converted to a DOMString using the <a>utf-8 decode without BOM</a> algorithm.
      </li>

      <li>
        An optional <a>ATT Bearer</a>, over which all GATT communication happens.
        The <a>ATT Bearer</a> is created by procedures described in
        "Connection Establishment" under <a>GAP Interoperability Requirements</a>.
        It is disconnected in ways [[BLUETOOTH42]] isn't entirely clear about.
      </li>

      <li>
        A list of advertised <a>Service UUIDs</a>
        from the <a>Advertising Data</a> or <a>Extended Inquiry Response</a>.
      </li>

      <li>
        A hierarchy of GATT attributes, described in [[#information-model]].
      </li>
    </ul>

    <p>
      The UA SHOULD determine that
      two <a>Bluetooth device</a>s are
      the <dfn export local-lt="same device">same Bluetooth device</dfn> if and only if
      they have the same <a>Public Bluetooth Address</a>, <a>Static Address</a>,
      <a>Private Address</a>, or <a>Identity Resolving Key</a>,
      or if the <a>Resolvable Private Address Resolution Procedure</a> succeeds using
      one device's IRK and the other's Resolvable <a>Private Address</a>.
      However, because platform APIs don't document how they determine device identity,
      the UA MAY use another procedure.
    </p>
  </section>

  <section>
    <h3 id="bluetoothdevice" dfn-type="interface">BluetoothDevice</h3>

    <p>
      A {{BluetoothDevice}} instance represents a <a>Bluetooth device</a>
      for a particular <a>global object</a>
      (or, equivalently, for a particular <a>Realm</a> or {{Bluetooth}} instance).
    </p>

    <pre class="idl">
      interface BluetoothDevice {
        readonly attribute DOMString id;
        readonly attribute DOMString? name;
        readonly attribute BluetoothRemoteGATTServer? gatt;

        Promise&lt;void> watchAdvertisements();
        void unwatchAdvertisements();
        readonly attribute boolean watchingAdvertisements;
      };
      BluetoothDevice implements EventTarget;
      BluetoothDevice implements BluetoothDeviceEventHandlers;
      BluetoothDevice implements CharacteristicEventHandlers;
      BluetoothDevice implements ServiceEventHandlers;
    </pre>

    <div class="note" heading="{{BluetoothDevice}} attributes"
         dfn-for="BluetoothDevice" dfn-type="attribute">
      <p>
        <dfn>id</dfn> uniquely identifies a device to the extent that
        the UA can determine that two Bluetooth connections are to the same device
        and to the extent that
        the user <a href="#note-device-id-tracking">wants to expose that fact to script</a>.
      </p>

      <p>
        <dfn>name</dfn> is the human-readable name of the device.
      </p>

      <p>
        {{BluetoothDevice/gatt}} provides a way to interact with this device's GATT server
        if the site has permission to do so.
      </p>

      <p>
        <dfn>watchingAdvertisements</dfn> is true if
        the UA is currently scanning for advertisements from this device
        and firing events for them.
      </p>
    </div>

    <p>
      Instances of {{BluetoothDevice}} are created with the <a>internal slots</a>
      described in the following table:
    </p>
    <table class="data" dfn-for="BluetoothDevice" dfn-type="attribute">
      <thead>
        <th><a>Internal Slot</a></th>
        <th>Initial Value</th>
        <th>Description (non-normative)</th>
      </thead>
      <tr>
        <td><dfn>\[[context]]</dfn></td>
        <td>&lt;always set in prose></td>
        <td>
          The {{Bluetooth}} object that returned this {{BluetoothDevice}}.
        </td>
      </tr>
      <tr>
        <td><dfn>\[[representedDevice]]</dfn></td>
        <td>&lt;always set in prose></td>
        <td>
          The <a>Bluetooth device</a> this object represents,
          or `null` if access has been <a lt="revoke Bluetooth access">revoked</a>.
        </td>
      </tr>
      <tr>
        <td><dfn>\[[gatt]]</dfn></td>
        <td>
          a new {{BluetoothRemoteGATTServer}} instance with
          its {{BluetoothRemoteGATTServer/device}} attribute initialized to `this`
          and its {{BluetoothRemoteGATTServer/connected}} attribute initialized to `false`.
        </td>
        <td>
          Does not change.
        </td>
      </tr>
      <tr>
        <td><dfn>\[[allowedServices]]</dfn></td>
        <td>&lt;always set in prose></td>
        <td>
          This device's {{AllowedBluetoothDevice/allowedServices}} list for this origin
          or `"all"` if all services are allowed.
          For example, a UA may grant an origin access to all services on
          a {{referringDevice}} that advertised a URL on that origin.
        </td>
      </tr>
    </table>

    <div algorithm>
      <p>
        To <dfn export>get the <code>BluetoothDevice</code> representing</dfn>
        a <a>Bluetooth device</a> <var>device</var>
        inside a {{Bluetooth}} instance <var>context</var>,
        the UA MUST run the following steps:
      </p>
      <ol>
        <li>
          Let |data|, a {{BluetoothPermissionData}},
          be {{"bluetooth"}}'s <a>extra permission data</a>
          for the <a>current settings object</a>.
        </li>
        <li>
          Find the |allowedDevice| in <code>|data|.{{allowedDevices}}</code>
          with <code><var>allowedDevice</var>.{{[[device]]}}</code>
          the <a>same device</a> as <var>device</var>.
          If there is no such object,
          throw a {{SecurityError}} and abort these steps.
        </li>
        <li>
          If there is no key in
          <var>context</var>.{{Bluetooth/[[deviceInstanceMap]]}}
          that is the <a>same device</a> as <var>device</var>,
          run the following sub-steps:
          <ol>
            <li>Let <var>result</var> be a new instance of {{BluetoothDevice}}.</li>
            <li>Initialize all of <var>result</var>'s optional fields to <code>null</code>.</li>
            <li>
              Initialize <code><var>result</var>.{{[[context]]}}</code>
              to <var>context</var>.
            </li>
            <li>
              Initialize <code><var>result</var>.{{[[representedDevice]]}}</code>
              to <var>device</var>.
            </li>
            <li>
              Initialize <code><var>result</var>.id</code>
              to <code><var>allowedDevice</var>.{{AllowedBluetoothDevice/deviceId}}</code>,
              and initialize <code><var>result</var>.{{BluetoothDevice/[[allowedServices]]}}</code>
              to <code><var>allowedDevice</var>.{{allowedServices}}</code>.
            </li>
            <li>
              If <var>device</var> has a partial or complete <a>Bluetooth Device Name</a>,
              set <code><var>result</var>.name</code> to that string.
            </li>
            <li>
              Initialize <code><var>result</var>.watchingAdvertisements</code> to `false`.
            </li>
            <li>
              Add a mapping from <var>device</var> to <var>result</var>
              in <var>context</var>.{{Bluetooth/[[deviceInstanceMap]]}}.
            </li>
          </ol>
        </li>
        <li>
          Return the value in
          <var>context</var>.{{Bluetooth/[[deviceInstanceMap]]}}
          whose key is the <a>same device</a> as <var>device</var>.
        </li>
      </ol>
    </div>

    <div algorithm>
      <p>
        Getting the <code><dfn attribute for="BluetoothDevice">gatt</dfn></code> attribute
        MUST perform the following steps:
      </p>
      <ol>
        <li>
          If {{"bluetooth"}}'s <a>extra permission data</a>
          for `this`'s <a>relevant settings object</a>
          has an {{AllowedBluetoothDevice}} |allowedDevice|
          in its {{BluetoothPermissionData/allowedDevices}} list
          with <code>|allowedDevice|.{{AllowedBluetoothDevice/[[device]]}}</code>
          the <a>same device</a> as <code>this.{{[[representedDevice]]}}</code>
          and <code>|allowedDevice|.{{AllowedBluetoothDevice/mayUseGATT}}</code>
          equal to `true`,
          return <code>this.{{[[gatt]]}}</code>.
        </li>
        <li>
          Otherwise, return `null`.
        </li>
      </ol>
    </div>

    <p class="note">
      Scanning costs power,
      so websites should avoid watching for advertisements unnecessarily,
      and should call {{unwatchAdvertisements()}} to stop using power as soon as possible.
    </p>

    <div algorithm class="unstable">
      <p>
        The <code><dfn method for="BluetoothDevice">watchAdvertisements()</dfn></code> method,
        when invoked,
        MUST return <a>a new promise</a> |promise| and
        run the following steps <a>in parallel</a>:
      </p>
      <ol>
        <li>
          Ensure that the UA is scanning for this device's advertisements.
          The UA SHOULD NOT filter out "duplicate" advertisements for the same device.
        </li>
        <li>
          If the UA fails to enable scanning,
          <a>reject</a> |promise| with one of the following errors,
          and abort these steps:

          <dl class="switch">
            <dt>The UA doesn't support scanning for advertisements</dt>
            <dd>{{NotSupportedError}}</dd>

            <dt>Bluetooth is turned off</dt>
            <dd>{{InvalidStateError}}</dd>

            <dt>Other reasons</dt>
            <dd>{{UnknownError}}</dd>
          </dl>
        </li>
        <li>
          <a>Queue a task</a> to perform the following steps:
          <ol>
            <li>Set <code>this.{{watchingAdvertisements}}</code> to `true`.</li>
            <li>Resolve |promise| with `undefined`.</li>
          </ol>
        </li>
      </ol>
    </div>

    <div algorithm class="unstable">
      <p>
        The <code><dfn method for="BluetoothDevice">unwatchAdvertisements()</dfn></code> method,
        when invoked,
        MUST run the following steps:
      </p>
      <ol>
        <li>Set <code>this.{{watchingAdvertisements}}</code> to `false`.</li>
        <li>
          If no more {{BluetoothDevice}}s in the whole UA
          have {{watchingAdvertisements}} set to `true`,
          the UA SHOULD stop scanning for advertisements.
          Otherwise, if no more {{BluetoothDevice}}s representing the same device as `this`
          have {{watchingAdvertisements}} set to `true`,
          the UA SHOULD reconfigure the scan to avoid receiving reports for this device.
        </li>
      </ol>
    </div>

    <section class="unstable">
      <h4 id="advertising-events">Responding to Advertising Events</h4>

      <p>
        When an <a>advertising event</a> arrives for a {{BluetoothDevice}}
        with {{BluetoothDevice/watchingAdvertisements}} set,
        the UA delivers an "{{advertisementreceived}}" event.
      </p>

      <pre class="idl">
        interface BluetoothManufacturerDataMap {
          readonly maplike&lt;unsigned short, DataView>;
        };
        interface BluetoothServiceDataMap {
          readonly maplike&lt;UUID, DataView>;
        };
        [Constructor(DOMString type, BluetoothAdvertisingEventInit init)]
        interface BluetoothAdvertisingEvent : Event {
          [SameObject]
          readonly attribute BluetoothDevice device;
          readonly attribute FrozenArray&lt;UUID> uuids;
          readonly attribute DOMString? name;
          readonly attribute unsigned short? appearance;
          readonly attribute byte? txPower;
          readonly attribute byte? rssi;
          [SameObject]
          readonly attribute BluetoothManufacturerDataMap manufacturerData;
          [SameObject]
          readonly attribute BluetoothServiceDataMap serviceData;
        };
        dictionary BluetoothAdvertisingEventInit : EventInit {
          required BluetoothDevice device;
          sequence&lt;(DOMString or unsigned long)> uuids;
          DOMString name;
          unsigned short appearance;
          byte txPower;
          byte rssi;
          Map manufacturerData;
          Map serviceData;
        };
      </pre>

      <div class="note" heading="{{BluetoothAdvertisingEvent}} attributes"
           dfn-for="BluetoothAdvertisingEvent" dfn-type="attribute"
           link-for-hint="BluetoothAdvertisingEvent">
        <p>
          <dfn>device</dfn> is the {{BluetoothDevice}} that sent this advertisement.
        </p>
        <p>
          <dfn>uuids</dfn> lists the Service UUIDs that
          this advertisement says {{device}}'s GATT server supports.
        </p>
        <p>
          <dfn>name</dfn> is {{device}}'s local name, or a prefix of it.
        </p>
        <p>
          <dfn>appearance</dfn> is an <a>Appearance</a>, one of the values defined by
          the {{org.bluetooth.characteristic.gap.appearance}} characteristic.
        </p>
        <p>
          <dfn>txPower</dfn> is
          the transmission power at which the device is broadcasting, measured in dBm.
          This is used to compute the path loss as <code>this.txPower - this.rssi</code>.
        </p>
        <p>
          <dfn>rssi</dfn> is
          the power at which the advertisement was received, measured in dBm.
          This is used to compute the path loss as <code>this.txPower - this.rssi</code>.
        </p>
        <p>
          <dfn>manufacturerData</dfn> maps <code>unsigned short</code> Company Identifier Codes
          to {{DataView}}s.
        </p>
        <p>
          <dfn>serviceData</dfn> maps {{UUID}}s to {{DataView}}s.
        </p>
      </div>

      <div class="example" id="example-interpret-ibeacon">
        <p>
          To retrieve a device and read the iBeacon data out of it,
          a developer could use the following code.
          Note that this API currently doesn't provide a way
          to request devices with certain manufacturer data,
          so the iBeacon will need to rotate its advertisements to include a known service
          in order for users to select this device in the <code>requestDevice</code> dialog.
        </p>

        <pre highlight="js">
          var known_service = "A service in the iBeacon's GATT server";
          return navigator.bluetooth.requestDevice({
            filters: [{services: [known_service]}]
          }).then(device => {
            device.watchAdvertisements();
            device.addEventListener('advertisementreceived', interpretIBeacon);
          });

          function interpretIBeacon(event) {
            var rssi = event.rssi;
            var appleData = event.manufacturerData.get(<a
                href="https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers"
                title="Apple, Inc.'s Company Identifier">0x004C</a>);
            if (appleData.byteLength != 23 ||
              appleData.getUint16(0, false) !== 0x0215) {
              console.log({isBeacon: false});
            }
            var uuidArray = new Uint8Array(appleData.buffer, 2, 16);
            var major = appleData.getUint16(18, false);
            var minor = appleData.getUint16(20, false);
            var txPowerAt1m = -appleData.getInt8(22);
            console.log({
                isBeacon: true,
                uuidArray,
                major,
                minor,
                pathLossVs1m: txPowerAt1m - rssi});
          });
        </pre>

        <p>
          The format of iBeacon advertisements was derived from
          <a href="http://www.warski.org/blog/2014/01/how-ibeacons-work/">How do iBeacons work?</a>
          by Adam Warski.
        </p>
      </div>

      <div algorithm="received advertising event" id="advertising-event-algorithm">
        <p>
          When the UA receives an <a>advertising event</a>
          (consisting of an advertising packet and an optional scan response),
          it MUST run the following steps:
        </p>
        <ol>
          <li>
            Let <var>device</var> be the <a>Bluetooth device</a> that sent the advertising event.
          </li>
          <li>
            For each {{BluetoothDevice}} <var>deviceObj</var> in the UA
            such that <var>device</var> is the <a>same device</a> as
            <code><var>deviceObj</var>.{{[[representedDevice]]}}</code>,
            <a>queue a task</a> on
            <var>deviceObj</var>'s <a>relevant settings object</a>'s <a>responsible event loop</a>
            to do the following sub-steps:
            <ol>
              <li>
                If <code><var>deviceObj</var>.{{watchingAdvertisements}}</code> is `false`,
                abort these sub-steps.
              </li>
              <li>
                <a>Fire an `advertisementreceived` event</a>
                for the advertising event at |deviceObj|.
              </li>
            </ol>
          </li>
        </ol>
      </div>

      <div algorithm>
        <p>
          To <dfn export>fire an {{advertisementreceived}} event</dfn>
          for an advertising event |adv| at a {{BluetoothDevice}} |deviceObj|,
          the UA MUST perform the following steps:
        </p>
        <ol>
          <li>
            Let <var>event</var> be
            <pre highlight="js">
              {
                bubbles: true,
                device: <var>deviceObj</var>,
                uuids: [],
                manufacturerData: new Map(),
                serviceData: new Map()
              }
            </pre>
          </li>
          <li>
            If the <a>received signal strength</a> is available
            for any packet in |adv|,
            set <code><var>event</var>.rssi</code> to this signal strength in dBm.
          </li>
          <li>
            For each <a>AD structure</a> in |adv|'s advertising packet and scan response,
            select from the following steps depending on the AD type:
            <dl class="switch">
              <dt>Incomplete List of 16-bit <a lt="Service UUID Data Type">Service UUIDs</a></dt>
              <dt>Complete List of 16-bit <a lt="Service UUID Data Type">Service UUIDs</a></dt>
              <dt>Incomplete List of 32-bit <a lt="Service UUID Data Type">Service UUIDs</a></dt>
              <dt>Complete List of 32-bit <a lt="Service UUID Data Type">Service UUIDs</a></dt>
              <dt>Incomplete List of 128-bit <a lt="Service UUID Data Type">Service UUIDs</a></dt>
              <dt>Complete List of 128-bit <a lt="Service UUID Data Type">Service UUIDs</a></dt>
              <dd>Append the listed UUIDs to <code><var>event</var>.uuids</code>.</dd>

              <dt>Shortened <a lt="Local Name Data Type">Local Name</a></dt>
              <dt>Complete <a lt="Local Name Data Type">Local Name</a></dt>
              <dd>
                <a>UTF-8 decode without BOM</a> the AD data and
                set <code><var>event</var>.name</code> to the result.

                Note: We don't expose whether the name is complete
                because existing APIs require
                reading the raw advertisement to get this information,
                and we want more evidence that it's useful
                before adding a field to the API.
              </dd>

              <dt><a>Manufacturer Specific Data</a></dt>
              <dd>
                Add to <code><var>event</var>.manufacturerData</code>
                a mapping from the 16-bit Company Identifier Code to
                an {{ArrayBuffer}} containing the manufacturer-specific data.
              </dd>

              <dt><a>TX Power Level</a></dt>
              <dd>
                Set <code><var>event</var>.txPower</code> to the AD data.
              </dd>

              <dt><a>Service Data</a> - 16 bit UUID</dt>
              <dt><a>Service Data</a> - 32 bit UUID</dt>
              <dt><a>Service Data</a> - 128 bit UUID</dt>
              <dd>
                Add to <code><var>event</var>.serviceData</code>
                a mapping from the <a>UUID</a> to
                an {{ArrayBuffer}} containing the service data.
              </dd>

              <dt><a>Appearance</a></dt>
              <dd>
                Set <code><var>event</var>.appearance</code> to the AD data.
              </dd>

              <dt>Otherwise</dt>
              <dd>Skip to the next AD structure.</dd>
            </dl>
          </li>
          <li>
            <a>Fire an event</a> initialized as
            <code>new {{BluetoothAdvertisingEvent}}("{{advertisementreceived}}",
              <var>event</var>)</code>,
            with its {{Event/isTrusted}} attribute initialized to `true`,
            at <var>deviceObj</var>.
          </li>
        </ol>
      </div>

      <p>
        All fields in {{BluetoothAdvertisingEvent}} return
        the last value they were initialized or set to.
      </p>

      <div algorithm>
        <p>
          The <dfn constructor for="BluetoothAdvertisingEvent">BluetoothAdvertisingEvent(type, init)</dfn>
          constructor MUST perform the following steps:
        </p>
        <ol>
          <li>
            Let <var>event</var> be the result of running the steps from [[dom#constructing-events]]
            except for the {{BluetoothAdvertisingEventInit/uuids}},
            {{BluetoothAdvertisingEventInit/manufacturerData}},
            and {{BluetoothAdvertisingEventInit/serviceData}} members.
          </li>
          <li>
            If <code><var>init</var>.uuids</code> is set,
            initialize <code><var>event</var>.uuids</code>
            to a new {{FrozenArray}} containing the elements of
            <code><var>init</var>.uuids.map({{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>.
            Otherwise initialize <code><var>event</var>.uuids</code> to an empty {{FrozenArray}}.
          </li>
          <li>
            For each mapping in <code><var>init</var>.manufacturerData</code>:
            <ol>
              <li>
                Let <var>code</var> be the key converted to an {{unsigned short}}.
              </li>
              <li>
                Let <var>value</var> be the value.
              </li>
              <li>
                If <var>value</var> is not a {{BufferSource}}, throw a {{TypeError}}.
              </li>
              <li>
                Let <var>bytes</var> be a new <a>read only ArrayBuffer</a>
                containing <a>a copy of the bytes held</a> by <var>value</var>.
              </li>
              <li>
                Add a mapping from <var>code</var> to <code>new DataView(<var>bytes</var>)</code>
                in <code><var>event</var>.manufacturerData.{{BluetoothManufacturerDataMap/[[BackingMap]]}}</code>.
              </li>
            </ol>
          </li>
          <li>
            For each mapping in <code><var>init</var>.serviceData</code>:
            <ol>
              <li>
                Let <var>key</var> be the key.
              </li>
              <li>
                Let <var>service</var> be the result of calling
                <code>BluetoothUUID.{{BluetoothUUID/getService()|getService}}(<var>key</var>).</code>
              </li>
              <li>
                Let <var>value</var> be the value.
              </li>
              <li>
                Set <var>value</var> is not a {{BufferSource}}, throw a {{TypeError}}.
              </li>
              <li>
                Let <var>bytes</var> be a new <a>read only ArrayBuffer</a>
                containing <a>a copy of the bytes held</a> by <var>value</var>.
              </li>
              <li>
                Add a mapping from <var>service</var> to <code>new DataView(<var>bytes</var>)</code>
                in <code><var>event</var>.serviceData.{{BluetoothServiceDataMap/[[BackingMap]]}}</code>.
              </li>
            </ol>
          </li>
          <li>Return <var>event</var>.</li>
        </ol>
      </div>

      <section>
        <h5 dfn-type="interface">BluetoothManufacturerDataMap</h5>

        <p>
          Instances of {{BluetoothManufacturerDataMap}} have a
          <dfn for="BluetoothManufacturerDataMap" dfn-type="attribute">`[[BackingMap]]`</dfn>
          slot because they are [=maplike=], which maps manufacturer codes to
          the manufacturer's data, converted to {{DataView}}s.
        </p>
      </section>

      <section>
        <h5 dfn-type="interface">BluetoothServiceDataMap</h5>

        <p>
          Instances of {{BluetoothServiceDataMap}} have a
          <dfn for="BluetoothServiceDataMap" dfn-type="attribute">`[[BackingMap]]`</dfn>
          slot because they are [=maplike=], which maps service UUIDs to the
          service's data, converted to {{DataView}}s.
        </p>
      </section>
    </section>
  </section>
</section>

<section>
  <h2 id="gatt-interaction">GATT Interaction</h2>

  <section id="">
    <h3 id="information-model">GATT Information Model</h3>

    <div class="note">
      <p>
        The <a>GATT Profile Hierarchy</a> describes how a
        <a idl lt="BluetoothRemoteGATTServer">GATT Server</a>
        contains a hierarchy of Profiles, Primary <a>Service</a>s,
        <a>Included Service</a>s, <a>Characteristic</a>s, and <a>Descriptor</a>s.
      </p>
      <p>
        Profiles are purely logical:
        the specification of a Profile describes the expected interactions between
        the other GATT entities the Profile contains,
        but it's impossible to query which Profiles a device supports.
      </p>
      <p>
        <a>GATT Client</a>s can discover and interact with
        the Services, Characteristics, and Descriptors on a device
        using a set of <a>GATT procedures</a>.
        This spec refers to Services, Characteristics, and Descriptors
        collectively as <dfn>Attribute</dfn>s.
        All Attributes have a type that's identified by a <a>UUID</a>.
        Each Attribute also has a 16-bit <a>Attribute Handle</a>
        that distinguishes it from other Attributes
        of the same type on the same <a>GATT Server</a>.
        Attributes are notionally ordered within their <a>GATT Server</a>
        by their <a>Attribute Handle</a>,
        but while platform interfaces provide attributes in some order,
        they do not guarantee that it's consistent with the <a>Attribute Handle</a> order.
      </p>
      <p link-for-hint="BluetoothRemoteGATTService">
        A <a idl lt="BluetoothRemoteGATTService">Service</a> contains
        a collection of <a idl lt="getIncludedService()">Included Service</a>s
        and <a idl lt="getCharacteristic()">Characteristic</a>s.
        The Included Services are references to other Services,
        and a single Service can be included by more than one other Service.
        Services are known as
        <a idl lt="isPrimary">Primary Services</a> if they appear directly under the
        <a idl lt="BluetoothRemoteGATTServer">GATT Server</a>,
        and Secondary Services if they're only included by other Services,
        but Primary Services can also be included.
      </p>
      <p link-for-hint="BluetoothRemoteGATTCharacteristic">
        A <a idl lt="BluetoothRemoteGATTCharacteristic">Characteristic</a>
        contains a value, which is an array of bytes,
        and a collection of <a idl lt="getDescriptor()">Descriptor</a>s.
        Depending on the <a idl lt="BluetoothCharacteristicProperties">properties</a> of the Characteristic,
        a <a>GATT Client</a> can read or write its value,
        or register to be notified when the value changes.
      </p>
      <p>
        Finally, a <a idl lt="BluetoothRemoteGATTDescriptor">Descriptor</a>
        contains a value (again an array of bytes)
        that describes or configures its <a>Characteristic</a>.
      </p>
    </div>

    <section>
      <h4 id="persistence">Persistence across connections</h4>

      The Bluetooth <a>Attribute Caching</a> system allows <a>bonded</a> clients
      to save references to attributes from one connection to the next.
      Web Bluetooth treats websites as <em>not</em> <a>bonded</a>
      to devices they have permission to access:
      {{BluetoothRemoteGATTService}}, {{BluetoothRemoteGATTCharacteristic}},
      and {{BluetoothRemoteGATTDescriptor}} objects
      become invalid on <a href="#disconnection-events">disconnection</a>,
      and the site must retrieved them again when it re-connects.
    </section>

    <section>
      <h4 id="bluetooth-cache" dfn-type="dfn" data-lt="Bluetooth cache">The Bluetooth cache</h4>

      <p>
        The UA MUST maintain a <a>Bluetooth cache</a>
        of the hierarchy of Services, Characteristics, and Descriptors
        it has discovered on a device.
        The UA MAY share this cache between multiple origins accessing the same device.
        Each potential entry in the cache is either known-present, known-absent, or unknown.
        The cache MUST NOT contain two entries that are for the <a>same attribute</a>.
        Each known-present entry in the cache is associated with an optional
        <code>Promise&lt;{{BluetoothRemoteGATTService}}></code>,
        <code>Promise&lt;{{BluetoothRemoteGATTCharacteristic}}></code>,
        or <code>Promise&lt;{{BluetoothRemoteGATTDescriptor}}></code> instance
        for each {{Bluetooth}} instance.
      </p>

      <p class="note">
        For example, if a user calls the <code>serviceA.getCharacteristic(uuid1)</code> function
        with an initially empty <a>Bluetooth cache</a>,
        the UA uses the <a>Discover Characteristics by UUID</a> procedure
        to fill the needed cache entries,
        and the UA ends the procedure early because
        it only needs one Characteristic to fulfil the returned {{Promise}},
        then the first Characteristic with UUID <code>uuid1</code> inside <code>serviceA</code>
        is known-present,
        and any subsequent Characteristics with that UUID remain unknown.
        If the user later calls <code>serviceA.getCharacteristics(uuid1)</code>,
        the UA needs to resume or restart the <a>Discover Characteristics by UUID</a> procedure.
        If it turns out that
        <code>serviceA</code> only has one Characteristic with UUID <code>uuid1</code>,
        then the subsequent Characteristics become known-absent.
      </p>

      <p>
        The known-present entries in the <a>Bluetooth cache</a> are ordered:
        Primary Services appear in a particular order within a device,
        Included Services and Characteristics appear in a particular order within Services,
        and Descriptors appear in a particular order within Characteristics.
        The order SHOULD match the order of <a>Attribute Handle</a>s on the device,
        but UAs MAY use another order if the device's order isn't available.
      </p>

      <div algorithm>
        <p>
          To <dfn>populate the Bluetooth cache</dfn> with entries matching some description,
          the UA MUST run the following steps.
          <span class="note">Note that these steps can block,
            so uses of this algorithm must be <a>in parallel</a>.</span>
        </p>
        <ol>
          <li>
            Attempt to make all matching entries in the cache either known-present or known-absent,
            using any sequence of <a>GATT procedures</a> that [[BLUETOOTH42]] specifies
            will return enough information.
            Handle errors as described in <a href="#error-handling"></a>.
          </li>
          <li>
            If the previous step returns an error, return that error from this algorithm.
          </li>
        </ol>
      </div>

      <div algorithm>
        <p>
          To <dfn>query the Bluetooth cache</dfn> in
          a {{BluetoothDevice}} instance <var>deviceObj</var>
          for entries matching some description,
          the UA MUST return a
          <code><var>deviceObj</var>.gatt</code>-<a>connection-checking wrapper</a> around
          <a>a new promise</a> <var>promise</var>
          and run the following steps <a>in parallel</a>:
        </p>
        <ol>
          <li>
            <a>Populate the Bluetooth cache</a> with entries matching the description.
          </li>
          <li>
            If the previous step returns an error,
            <a>reject</a> <var>promise</var> with that error and abort these steps.
          </li>
          <li>
            Let <var>entries</var> be the sequence
            of known-present cache entries matching the description.
          </li>
          <li>
            Let <var>context</var> be
            <code><var>deviceObj</var>.{{BluetoothDevice/[[context]]}}</code>.
          </li>
          <li>
            Let <var>result</var> be a new sequence.
          </li>
          <li>
            For each <var>entry</var> in <var>entries</var>:
            <ol>
              <li>
                If <var>entry</var> has no associated <code>Promise&lt;BluetoothGATT*></code> instance
                in <var>context</var>.{{Bluetooth/[[attributeInstanceMap]]}},
                <a>create a <code>BluetoothRemoteGATTService</code>
                representing</a> <var>entry</var>,
                <a>create a <code>BluetoothRemoteGATTCharacteristic</code>
                representing</a> <var>entry</var>,
                or <a>create a <code>BluetoothRemoteGATTDescriptor</code>
                representing</a> <var>entry</var>,
                depending on whether <var>entry</var> is a Service, Characteristic, or Descriptor,
                and add a mapping from <var>entry</var> to the resulting <code>Promise</code>
                in <var>context</var>.{{Bluetooth/[[attributeInstanceMap]]}}.
              </li>
              <li>
                Append to <var>result</var>
                the <code>Promise&lt;BluetoothGATT*></code> instance
                associated with <var>entry</var>
                in <var>context</var>.{{Bluetooth/[[attributeInstanceMap]]}}.
              </li>
            </ol>
          </li>
          <li>
            <a>Resolve</a> <var>promise</var> with the result of
            <a>waiting for all</a> elements of <var>result</var>.
          </li>
        </ol>
      </div>

      <div algorithm>
        <p>
          <dfn>Represented</dfn>(|obj|: Device or GATT Attribute) returns,
          depending on the type of |obj|:
        </p>
        <dl class="switch">
          <dt>{{BluetoothDevice}}</dt>
          <dd><code>|obj|.{{[[representedDevice]]}}</code></dd>

          <dt>{{BluetoothRemoteGATTService}}</dt>
          <dd><code>|obj|.{{[[representedService]]}}</code></dd>

          <dt>{{BluetoothRemoteGATTCharacteristic}}</dt>
          <dd><code>|obj|.{{[[representedCharacteristic]]}}</code></dd>

          <dt>{{BluetoothRemoteGATTDescriptor}}</dt>
          <dd><code>|obj|.{{[[representedDescriptor]]}}</code></dd>
        </dl>
      </div>
    </section>

    <section>
      <h4 id="navigating-bluetooth-hierarchy">Navigating the Bluetooth Hierarchy</h4>

      <div algorithm>
        <p>
          To <dfn>GetGATTChildren</dfn>(<span class="argument-list"><var>attribute</var>: GATT Attribute,<br>
          <var>single</var>: boolean,<br>
          <var>uuidCanonicalizer</var>: function,<br>
          <var>uuid</var>: optional <code>(DOMString or unsigned int)</code>,<br>
          <var>allowedUuids</var>: optional <code>("all" or Array&lt;DOMString>)</code>,<br>
          <var>child type</var>: GATT declaration type),</span><br>
          the UA MUST perform the following steps:
        </p>
        <ol>
          <li>
            If <var>uuid</var> is present,
            set it to <var>uuidCanonicalizer</var>(<var>uuid</var>).
            If <var>uuidCanonicalizer</var> threw an exception,
            return <a>a promise rejected with</a> that exception and abort these steps.
          </li>
          <li>
            If <var>uuid</var> is present and is <a>blocklisted</a>,
            return <a>a promise rejected with</a> a {{SecurityError}}
            and abort these steps.
          </li>
          <li>
            Let <var>deviceObj</var> be, depending on the type of <var>attribute</var>:
            <dl class="switch">
              <dt>{{BluetoothDevice}}</dt>
              <dd><code><var>attribute</var></code></dd>
              <dt>{{BluetoothRemoteGATTService}}</dt>
              <dd><code><var>attribute</var>.{{BluetoothRemoteGATTService/device}}</code></dd>
              <dt>{{BluetoothRemoteGATTCharacteristic}}</dt>
              <dd><code><var>attribute</var>.{{BluetoothRemoteGATTCharacteristic/service}}.{{BluetoothRemoteGATTService/device}}</code></dd>
            </dl>
          </li>
          <li>
            If <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
            is `false`,
            return <a>a promise rejected with</a> with a {{NetworkError}} and
            abort these steps.
          </li>
          <li>
            If <a>Represented</a>(|attribute|) is `null`,
            return <a>a promise rejected with</a> an {{InvalidStateError}} and
            abort these steps.

            Note: This happens when a Service or Characteristic is removed from the device
            or invalidated by a disconnection,
            and then its object is used again.
          </li>
          <li>
            <a>Query the Bluetooth cache</a> in <code><var>deviceObj</var></code>
            for entries that:
            <ul>
              <li>are within <a>Represented</a>(|attribute|),</li>
              <li>have a type described by <var>child type</var>,</li>
              <li>have a UUID that is not <a>blocklisted</a>,</li>
              <li>if <var>uuid</var> is present, have a UUID of <var>uuid</var>,</li>
              <li>
                if <var>allowedUuids</var> is present and not `"all"`,
                have a UUID in <var>allowedUuids</var>, and
              </li>
              <li>if the <var>single</var> flag is set, are the first of these.</li>
            </ul>
            Let <var>promise</var> be the result.
          </li>
          <li>
            Return the result of
            <a>transforming</a> <var>promise</var> with a fulfillment handler that:
            <ul>
              <li>If its argument is empty, throws a {{NotFoundError}},</li>
              <li>
                Otherwise, if the <var>single</var> flag is set,
                returns the first (only) element of its argument.
              </li>
              <li>Otherwise, returns its argument.</li>
            </ul>
          </li>
        </ol>
      </div>
    </section>

    <section>
      <h4 id="identifying-attributes">Identifying Services, Characteristics, and Descriptors</h4>

      <p>
        When checking whether two Services, Characteristics, or Descriptors
        <var>a</var> and <var>b</var> are the <dfn>same attribute</dfn>,
        the UA SHOULD determine that they are the same if <var>a</var> and <var>b</var>
        are inside the <a>same device</a> and have the same <a>Attribute Handle</a>,
        but MAY use any algorithm it wants with the constraint that
        <var>a</var> and <var>b</var> MUST NOT be considered the <a>same attribute</a> if
        they fit any of the following conditions:
      </p>
      <ul>
        <li>They are not both Services, both Characteristics, or both Descriptors.</li>
        <li>They are both Services, but are not both primary or both secondary services.</li>
        <li>They have different UUIDs.</li>
        <li>
          Their parent Devices aren't the <a>same device</a> or
          their parent Services or Characteristics aren't the <a>same attribute</a>.
        </li>
      </ul>

      <p class="note">
        This definition is loose because platform APIs expose their own notion of identity
        without documenting whether it's based on <a>Attribute Handle</a> equality.
      </p>

      <p class="note">
        For two Javascript objects <var>x</var> and <var>y</var>
        representing Services, Characteristics, or Descriptors,
        <code><var>x</var> === <var>y</var></code> returns
        whether the objects represent the <a>same attribute</a>,
        because of how the <a>query the Bluetooth cache</a> algorithm
        creates and caches new objects.
      </p>
    </section>
  </section>

  <section link-for-hint="BluetoothRemoteGATTServer">
    <h3 oldids="bluetoothgattremoteserver"
        dfn-type="interface">BluetoothRemoteGATTServer</h3>

    <p>
      {{BluetoothRemoteGATTServer}} represents a <a>GATT Server</a> on a remote device.
    </p>

    <pre class="idl">
      interface BluetoothRemoteGATTServer {
        [SameObject]
        readonly attribute BluetoothDevice device;
        readonly attribute boolean connected;
        Promise&lt;BluetoothRemoteGATTServer> connect();
        void disconnect();
        Promise&lt;BluetoothRemoteGATTService> getPrimaryService(BluetoothServiceUUID service);
        Promise&lt;sequence&lt;BluetoothRemoteGATTService>>
          getPrimaryServices(optional BluetoothServiceUUID service);
      };
    </pre>

    <div class="note" heading="{{BluetoothRemoteGATTServer}} attributes"
         dfn-for="BluetoothRemoteGATTServer" dfn-type="attribute">
      <p>
        <dfn>device</dfn> is the device running this server.
      </p>

      <p>
        <dfn>connected</dfn> is true while this instance
        is connected to <code>this.device</code>.
        It can be false while the UA is physically connected,
        for example when there are other connected {{BluetoothRemoteGATTServer}} instances
        for other <a>global object</a>s.
      </p>
    </div>

    <p>
      When no ECMAScript code can
      observe an instance of {{BluetoothRemoteGATTServer}} <var>server</var> anymore,
      the UA SHOULD run <code><var>server</var>.{{BluetoothRemoteGATTServer/disconnect()}}</code>.
      <span class="note">
        Because {{BluetoothDevice}} instances are stored in
        <code>navigator.bluetooth.{{Bluetooth/[[deviceInstanceMap]]}}</code>,
        this can't happen at least until navigation releases the global object or
        closing the tab or window destroys the <a>browsing context</a>.
      </span>
      <span class="note">
        Disconnecting on garbage collection ensures that
        the UA doesn't keep consuming resources on the remote device unnecessarily.
      </span>
    </p>

    <p>
      Instances of {{BluetoothRemoteGATTServer}} are created with the <a>internal slots</a>
      described in the following table:
    </p>

    <table class="data" dfn-for="BluetoothRemoteGATTServer" dfn-type="attribute">
      <thead>
        <th><a>Internal Slot</a></th>
        <th>Initial Value</th>
        <th>Description (non-normative)</th>
      </thead>
      <tr>
        <td><dfn>\[[activeAlgorithms]]</dfn></td>
        <td><code>new {{Set}}()</code></td>
        <td>
          Contains a {{Promise}} corresponding to each algorithm using this server's connection.
          {{disconnect()}} empties this set so that
          the algorithm can tell whether its <a>realm</a> was ever disconnected while it was running.
        </td>
      </tr>
    </table>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTServer">connect()</dfn></code> method, when invoked,
        MUST perform the following steps:
      </p>
      <ol>
        <li>Let |promise| be <a>a new promise</a>.</li>
        <li>
          If <code>this.device.{{[[representedDevice]]}}</code> is `null`,
          <a>queue a task</a> to <a>reject</a> |promise| with a {{NetworkError}},
          return |promise|,
          and abort these steps.
        </li>
        <li>
          If the UA is currently using the Bluetooth system,
          it MAY <a>queue a task</a> to <a>reject</a> |promise| with a {{NetworkError}},
          return |promise|,
          and abort these steps.

          Issue(188): Implementations may be able to avoid this {{NetworkError}},
          but for now sites need to serialize their use of this API
          and/or give the user a way to retry failed operations.
        </li>
        <li>
          Add |promise| to <code>this.{{[[activeAlgorithms]]}}</code>.
        </li>
        <li>
          Return |promise| and run the following steps <a>in parallel</a>:
          <ol>
            <li>
              If <code>this.device.{{[[representedDevice]]}}</code> has no <a>ATT Bearer</a>,
              do the following sub-steps:
              <ol>
                <li id="create-an-att-bearer">
                  Attempt to create an <a>ATT Bearer</a> using the procedures described
                  in "Connection Establishment" under <a>GAP Interoperability Requirements</a>.
                  Abort this attempt if |promise| is removed from <code>this.{{[[activeAlgorithms]]}}</code>.

                  Note: These procedures can wait forever
                  if a connectable advertisement isn't received.
                  The website should call {{disconnect()}}
                  if it no longer wants to connect.
                </li>
                <li>
                  If this attempt was aborted because
                  |promise| was removed from <code>this.{{[[activeAlgorithms]]}}</code>,
                  <a>reject</a> <var>promise</var> with an {{AbortError}} and abort these steps.
                </li>
                <li>
                  If this attempt failed for another reason,
                  <a>reject</a> <var>promise</var> with a {{NetworkError}} and abort these steps.
                </li>
                <li>
                  Use the <a>Exchange MTU</a> procedure to negotiate the largest supported MTU.
                  Ignore any errors from this step.
                </li>
                <li>
                  The UA MAY attempt to bond with the remote device using
                  the <a>BR/EDR Bonding Procedure</a> or
                  the <a>LE Bonding Procedure</a>.

                  Note: We would normally prefer to give the website control
                  over whether and when bonding happens,
                  but the [Core Bluetooth](https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/AboutCoreBluetooth/Introduction.html)
                  platform API doesn't provide a way for UAs to implement such a knob.
                  Having a bond is more secure than not having one,
                  so this specification allows the UA to opportunistically create one
                  on platforms where that's possible.
                  This may cause a user-visible pairing dialog to appear
                  when a connection is created,
                  instead of when a restricted characteristic is accessed.
                </li>
              </ol>
            </li>
            <li>
              <a>Queue a task</a> to perform the following sub-steps:
              <ol>
                <li>
                  If |promise| is not in <code>this.{{[[activeAlgorithms]]}}</code>,
                  <a>reject</a> <var>promise</var> with an {{AbortError}},
                  <a>garbage-collect the connection</a> of
                  <code>this.{{[[representedDevice]]}}</code>,
                  and abort these steps.
                </li>
                <li>
                  Remove |promise| from <code>this.{{[[activeAlgorithms]]}}</code>.
                </li>
                <li>
                  If <code>this.device.{{[[representedDevice]]}}</code> is `null`,
                  <a>reject</a> <var>promise</var> with a {{NetworkError}},
                  <a>garbage-collect the connection</a> of
                  <code>this.{{[[representedDevice]]}}</code>,
                  and abort these steps.
                </li>
                <li>
                  Set `this.connected` to `true`.
                </li>
                <li>
                  <a>Resolve</a> <var>promise</var> with <code>this</code>.
                </li>
              </ol>
            </li>
          </ol>
        </li>
      </ol>
    </div>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTServer">disconnect()</dfn></code> method, when invoked,
        MUST perform the following steps:
      </p>
      <ol>
        <li>
          Clear <code>this.{{[[activeAlgorithms]]}}</code>
          to abort any <a href="#create-an-att-bearer">active `connect()` calls</a>.
        <li>
          If <code>this.{{connected}}</code> is <code>false</code>, abort these steps.
        </li>
        <li>
          <a>Clean up the disconnected device</a> <code>this.device</code>.
        </li>
        <li>
          Let <var>device</var> be <code>this.device.{{[[representedDevice]]}}</code>.
        </li>
        <li>
          <a>Garbage-collect the connection</a> of |device|.
        </li>
      </ol>
    </div>

    <div algorithm>
      <p>
        To <dfn>garbage-collect the connection</dfn> of a |device|,
        the UA must, do the following steps <a>in parallel</a>:
      </p>
      <ol>
        <li>
          If systems that aren't using this API,
          either inside or outside of the UA,
          are using the <var>device</var>'s <a>ATT Bearer</a>,
          abort this algorithm.
        </li>
        <li>
          For all {{BluetoothDevice}}s <code><var>deviceObj</var></code> in the whole UA:
          <ol>
            <li>
              If <code><var>deviceObj</var>.{{[[representedDevice]]}}</code> is
              not the <a>same device</a> as <var>device</var>,
              continue to the next |deviceObj|.
            </li>
            <li>
              If <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
              is `true`,
              abort this algorithm.
            </li>
            <li>
              If <code><var>deviceObj</var>.gatt.{{[[activeAlgorithms]]}}</code>
              contains the {{Promise}} of a call to {{connect()}},
              abort this algorithm.
            </li>
          </ol>
        </li>
        <li>
          Destroy <var>device</var>'s <a>ATT Bearer</a>.
        </li>
      </ol>
    </div>

    <div algorithm>
      <p class="note">
        Algorithms need to fail if
        their {{BluetoothRemoteGATTServer}} was disconnected while they were running,
        even if the UA stays connected the whole time
        and the {{BluetoothRemoteGATTServer}} is subsequently re-connected before they finish.
        We wrap the returned {{Promise}} to accomplish this.
      </p>
      <p>
        To create a <var>gattServer</var>-<dfn>connection-checking wrapper</dfn>
        around a {{Promise}} <var>promise</var>, the UA MUST:
      </p>
      <ol>
        <li>
          If <code><var>gattServer</var>.connected</code> is `true`,
          add <var>promise</var> to
          <code><var>gattServer</var>.{{BluetoothRemoteGATTServer/[[activeAlgorithms]]}}</code>.
        </li>
        <li>
          Return the result of <a>transforming</a> <var>promise</var>
          with fulfillment and rejection handlers that perform the following steps:
          <dl>
            <dt>fulfillment handler</dt>
            <dd>
              <ol>
                <li>
                  If <var>promise</var> is in
                  <code><var>gattServer</var>.{{BluetoothRemoteGATTServer/[[activeAlgorithms]]}}</code>,
                  remove it and return the first argument.
                </li>
                <li>
                  Otherwise, throw a {{NetworkError}}.
                  <span class="note">
                    Because <var>gattServer</var> was disconnected
                    during the execution of the main algorithm.
                  </span>
                </li>
              </ol>
            </dd>
            <dt>rejection handler</dt>
            <dd>
              <ol>
                <li>
                  If <var>promise</var> is in
                  <code><var>gattServer</var>.{{BluetoothRemoteGATTServer/[[activeAlgorithms]]}}</code>,
                  remove it and throw the first argument.
                </li>
                <li>
                  Otherwise, throw a {{NetworkError}}.
                  <span class="note">
                    Because <var>gattServer</var> was disconnected
                    during the execution of the main algorithm.
                  </span>
                </li>
              </ol>
            </dd>
          </dl>
        </li>
      </ol>
    </div>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTServer">getPrimaryService(<var>service</var>)</dfn></code> method, when invoked,
        MUST perform the following steps:
      </p>
      <ol>
        <li>
          If <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code> is not `"all"`
          and <var>service</var> is not in
          <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>,
          return <a>a promise rejected with</a> a {{SecurityError}} and abort these steps.
        </li>
        <li>
        Return <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this.device</code>,<br>
          <i>single</i>=true,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
          <i>uuid</i>=<code><var>service</var></code>,<br>
          <i>allowedUuids</i>=<code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>,<br>
          <i>child type</i>="GATT Primary Service")</span>
        </li>
      </ol>
    </div>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTServer">getPrimaryServices(<var>service</var>)</dfn></code> method, when invoked,
        MUST perform the following steps:
      </p>
      <ol>
        <li>
          If <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code> is not `"all"`,
          and <var>service</var> is present
          and not in <code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>,
          return <a>a promise rejected with</a> a {{SecurityError}} and abort these steps.
        </li>
        <li>
          Return <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this.device</code>,<br>
          <i>single</i>=false,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
          <i>uuid</i>=<code><var>service</var></code>,<br>
          <i>allowedUuids</i>=<code>this.device.{{BluetoothDevice/[[allowedServices]]}}</code>,<br>
          <i>child type</i>="GATT Primary Service")</span>
        </li>
      </ol>
    </div>
  </section>

  <section>
    <h3 oldids="bluetoothgattservice"
        dfn-type="interface">BluetoothRemoteGATTService</h3>

    <p>
      {{BluetoothRemoteGATTService}} represents a GATT <a>Service</a>,
      a collection of characteristics and relationships to other services
      that encapsulate the behavior of part of a device.
    </p>

    <pre class="idl">
      interface BluetoothRemoteGATTService {
        [SameObject]
        readonly attribute BluetoothDevice device;
        readonly attribute UUID uuid;
        readonly attribute boolean isPrimary;
        Promise&lt;BluetoothRemoteGATTCharacteristic>
          getCharacteristic(BluetoothCharacteristicUUID characteristic);
        Promise&lt;sequence&lt;BluetoothRemoteGATTCharacteristic>>
          getCharacteristics(optional BluetoothCharacteristicUUID characteristic);
        Promise&lt;BluetoothRemoteGATTService>
          getIncludedService(BluetoothServiceUUID service);
        Promise&lt;sequence&lt;BluetoothRemoteGATTService>>
          getIncludedServices(optional BluetoothServiceUUID service);
      };
      BluetoothRemoteGATTService implements EventTarget;
      BluetoothRemoteGATTService implements CharacteristicEventHandlers;
      BluetoothRemoteGATTService implements ServiceEventHandlers;
    </pre>

    <div class="note" heading="{{BluetoothRemoteGATTService}} attributes"
         dfn-for="BluetoothRemoteGATTService" dfn-type="attribute">
      <p>
        <dfn>device</dfn> is the {{BluetoothDevice}} representing
        the remote peripheral that the GATT service belongs to.
      </p>
      <p>
        <dfn>uuid</dfn> is the UUID of the service,
        e.g. <code>'0000180d-0000-1000-8000-00805f9b34fb'</code> for the
        <a idl lt="org.bluetooth.service.heart_rate">Heart Rate</a> service.
      </p>
      <p>
        <dfn>isPrimary</dfn> indicates whether the type of this service is primary or secondary.
      </p>
    </div>

    <p>
      Instances of {{BluetoothRemoteGATTService}} are created with the <a>internal slots</a>
      described in the following table:
    </p>
    <table class="data" dfn-for="BluetoothRemoteGATTService" dfn-type="attribute">
      <thead>
        <th><a>Internal Slot</a></th>
        <th>Initial Value</th>
        <th>Description (non-normative)</th>
      </thead>
      <tr>
        <td><dfn>\[[representedService]]</dfn></td>
        <td>&lt;always set in prose></td>
        <td>
          The <a>Service</a> this object represents,
          or `null` if the Service has been removed or otherwise invalidated.
        </td>
      </tr>
    </table>

    <div algorithm>
      <p>
        To <dfn>create a <code>BluetoothRemoteGATTService</code> representing</dfn>
        a Service <var>service</var>,
        the UA must return <a>a new promise</a> <var>promise</var>
        and run the following steps <a>in parallel</a>.
      </p>
      <ol>
        <li>
          Let <var>result</var> be a new instance of {{BluetoothRemoteGATTService}}
          with its {{[[representedService]]}} slot initialized to |service|.
        </li>
        <li>
          <a>Get the <code>BluetoothDevice</code> representing</a>
          the device in which <var>service</var> appears,
          and let <var>device</var> be the result.
        </li>
        <li>
          If the previous step threw an error,
          <a>reject</a> <var>promise</var> with that error and abort these steps.
        </li>
        <li>
          Initialize <code><var>result</var>.device</code> from
          <var>device</var>.
        </li>
        <li>
          Initialize <code><var>result</var>.uuid</code> from the UUID of <var>service</var>.
        </li>
        <li>
          If <var>service</var> is a Primary Service,
          initialize <code><var>result</var>.isPrimary</code> to true.
          Otherwise initialize <code><var>result</var>.isPrimary</code> to false.
        </li>
        <li><a>Resolve</a> <var>promise</var> with <var>result</var>.</li>
      </ol>
    </div>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTService">getCharacteristic(<var>characteristic</var>)</dfn></code> method
        retrieves a <a>Characteristic</a> inside this Service.
        When invoked, it MUST return
      </p>
      <blockquote>
        <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
          <i>single</i>=true,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic}},<br>
          <i>uuid</i>=<code><var>characteristic</var></code>,<br>
          <i>allowedUuids</i>=<code>undefined</code>,<br>
          <i>child type</i>="GATT Characteristic")</span>
      </blockquote>
    </div>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTService">getCharacteristics(<var>characteristic</var>)</dfn></code> method
        retrieves a list of <a>Characteristic</a>s inside this Service.
        When invoked, it MUST return
      </p>
      <blockquote>
        <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
          <i>single</i>=false,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic}},<br>
          <i>uuid</i>=<code><var>characteristic</var></code>,<br>
          <i>allowedUuids</i>=<code>undefined</code>,<br>
          <i>child type</i>="GATT Characteristic")</span>
      </blockquote>
    </div>

    <div algorithm class="unstable">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTService">getIncludedService(<var>service</var>)</dfn></code> method
        retrieves an <a>Included Service</a> inside this Service.
        When invoked, it MUST return
      </p>
      <blockquote>
        <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
          <i>single</i>=true,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
          <i>uuid</i>=<code><var>service</var></code>,<br>
          <i>allowedUuids</i>=<code>undefined</code>,<br>
          <i>child type</i>="GATT Included Service")</span>
      </blockquote>
    </div>

    <div algorithm class="unstable">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTService">getIncludedServices(<var>service</var>)</dfn></code> method
        retrieves a list of <a>Included Service</a>s inside this Service.
        When invoked, it MUST return
      </p>
      <blockquote>
        <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
          <i>single</i>=false,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getService()|BluetoothUUID.getService}},<br>
          <i>uuid</i>=<code><var>service</var></code>,<br>
          <i>allowedUuids</i>=<code>undefined</code>,<br>
          <i>child type</i>="GATT Included Service")</span>
      </blockquote>
    </div>
  </section>

  <section>
    <h3 oldids="bluetoothgattcharacteristic"
        dfn-type="interface">BluetoothRemoteGATTCharacteristic</h3>

    <p>{{BluetoothRemoteGATTCharacteristic}} represents a GATT <a>Characteristic</a>, which is a basic data element that provides further information about a peripheral's service.</p>

    <pre class="idl">
      interface BluetoothRemoteGATTCharacteristic {
        [SameObject]
        readonly attribute BluetoothRemoteGATTService service;
        readonly attribute UUID uuid;
        readonly attribute BluetoothCharacteristicProperties properties;
        readonly attribute DataView? value;
        Promise&lt;BluetoothRemoteGATTDescriptor> getDescriptor(BluetoothDescriptorUUID descriptor);
        Promise&lt;sequence&lt;BluetoothRemoteGATTDescriptor>>
          getDescriptors(optional BluetoothDescriptorUUID descriptor);
        Promise&lt;DataView> readValue();
        Promise&lt;void> writeValue(BufferSource value);
        Promise&lt;BluetoothRemoteGATTCharacteristic> startNotifications();
        Promise&lt;BluetoothRemoteGATTCharacteristic> stopNotifications();
      };
      BluetoothRemoteGATTCharacteristic implements EventTarget;
      BluetoothRemoteGATTCharacteristic implements CharacteristicEventHandlers;
    </pre>

    <div class="note" heading="{{BluetoothRemoteGATTCharacteristic}} attributes"
         dfn-for="BluetoothRemoteGATTCharacteristic" dfn-type="attribute">
      <p>
        <dfn>service</dfn> is the GATT service this characteristic belongs to.
      </p>
      <p>
        <dfn>uuid</dfn> is the UUID of the characteristic,
        e.g. <code>'00002a37-0000-1000-8000-00805f9b34fb'</code> for the
        <a idl lt="org.bluetooth.characteristic.heart_rate_measurement"
           >Heart Rate Measurement</a> characteristic.
      </p>
      <p>
        <dfn>properties</dfn> holds the properties of this characteristic.
      </p>
      <p>
        <dfn>value</dfn> is the currently cached characteristic value.
        This value gets updated when the value of the characteristic is read or updated via a notification or indication.
      </p>
    </div>

    <p>
      Instances of {{BluetoothRemoteGATTCharacteristic}} are created with the <a>internal slots</a>
      described in the following table:
    </p>
    <table class="data" dfn-for="BluetoothRemoteGATTCharacteristic" dfn-type="attribute">
      <thead>
        <th><a>Internal Slot</a></th>
        <th>Initial Value</th>
        <th>Description (non-normative)</th>
      </thead>
      <tr>
        <td><dfn>\[[representedCharacteristic]]</dfn></td>
        <td>&lt;always set in prose></td>
        <td>
          The <a>Characteristic</a> this object represents,
          or `null` if the Characteristic has been removed or otherwise invalidated.
        </td>
      </tr>
    </table>

    <div algorithm>
      <p>
        To <dfn>create a <code>BluetoothRemoteGATTCharacteristic</code> representing</dfn>
        a Characteristic <var>characteristic</var>,
        the UA must return <a>a new promise</a> <var>promise</var>
        and run the following steps <a>in parallel</a>.
      </p>
      <ol>
        <li>
          Let <var>result</var> be a new instance of {{BluetoothRemoteGATTCharacteristic}}
          with its {{[[representedCharacteristic]]}} slot initialized to |characteristic|.
        </li>
        <li>
          Initialize <code><var>result</var>.service</code> from
          the {{BluetoothRemoteGATTService}} instance representing
          the Service in which <var>characteristic</var> appears.
        </li>
        <li>
          Initialize <code><var>result</var>.uuid</code> from
          the UUID of <var>characteristic</var>.
        </li>
        <li>
          <a>Create a <code>BluetoothCharacteristicProperties</code> instance
          from the Characteristic</a> <var>characteristic</var>,
          and let <var>propertiesPromise</var> be the result.
        </li>
        <li>Wait for <var>propertiesPromise</var> to settle.</li>
        <li>
          If <var>propertiesPromise</var> was rejected,
          <a>resolve</a> <var>promise</var> with <var>propertiesPromise</var> and
          abort these steps.
        </li>
        <li>
          Initialize <code><var>result</var>.properties</code> from
          the value <var>propertiesPromise</var> was fulfilled with.
        </li>
        <li>
          Initialize <code><var>result</var>.value</code> to <code>null</code>.
          The UA MAY initialize <code><var>result</var>.value</code> to
          a new {{DataView}} wrapping a new {{ArrayBuffer}} containing
          the most recently read value from <var>characteristic</var>
          if this value is available.
        </li>
        <li><a>Resolve</a> <var>promise</var> with <var>result</var>.</li>
      </ol>
    </div>

    <div algorithm class="unstable">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTCharacteristic">getDescriptor(<var>descriptor</var>)</dfn></code> method
        retrieves a <a>Descriptor</a> inside this Characteristic.
        When invoked, it MUST return
      </p>
      <blockquote>
        <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
          <i>single</i>=true,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor}},<br>
          <i>uuid</i>=<code><var>descriptor</var></code>,<br>
          <i>allowedUuids</i>=<code>undefined</code>,<br>
          <i>child type</i>="GATT Descriptor")</span>
      </blockquote>
    </div>

    <div algorithm class="unstable">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTCharacteristic">getDescriptors(<var>descriptor</var>)</dfn></code> method
        retrieves a list of <a>Descriptor</a>s inside this Characteristic.
        When invoked, it MUST return
      </p>
      <blockquote>
        <a>GetGATTChildren</a>(<span class="argument-list"><i>attribute</i>=<code>this</code>,<br>
          <i>single</i>=false,<br>
          <i>uuidCanonicalizer</i>={{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor}},<br>
          <i>uuid</i>=<code><var>descriptor</var></code>,<br>
          <i>allowedUuids</i>=<code>undefined</code>,<br>
          <i>child type</i>="GATT Descriptor")</span>
      </blockquote>
    </div>

    <div algorithm="Characteristic.readValue()">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTCharacteristic">readValue()</dfn></code> method, when invoked,
        MUST run the following steps:
      </p>
      <ol>
        <li>
          If <code>this.uuid</code> is <a>blocklisted for reads</a>,
          return <a>a promise rejected with</a> a {{SecurityError}}
          and abort these steps.
        </li>
        <li>
          If <code>this.service.device.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
          is `false`,
          return <a>a promise rejected with</a> a {{NetworkError}}
          and abort these steps.
        </li>
        <li>
          Let |characteristic| be
          <code>this.{{[[representedCharacteristic]]}}</code>.
        </li>
        <li>
          If |characteristic| is `null`,
          return <a>a promise rejected with</a> an {{InvalidStateError}} and
          abort these steps.
        </li>
        <li>
          Return a <code>this.service.device.gatt</code>-<a>connection-checking wrapper</a> around
          <a>a new promise</a> <var>promise</var>
          and run the following steps <a>in parallel</a>:
          <ol>
            <li>
              If the <code>Read</code> bit is not set
              in <var>characteristic</var>'s <a lt="Characteristic Properties">properties</a>,
              <a>reject</a> <var>promise</var> with a {{NotSupportedError}} and abort these steps.
            </li>
            <li>
              If the UA is currently using the Bluetooth system,
              it MAY <a>reject</a> |promise| with a {{NetworkError}}
              and abort these steps.

              Issue(188): Implementations may be able to avoid this {{NetworkError}},
              but for now sites need to serialize their use of this API
              and/or give the user a way to retry failed operations.
            </li>
            <li>
              Use any combination of the sub-procedures in
              the <a>Characteristic Value Read</a> procedure
              to retrieve the value of <var>characteristic</var>.
              Handle errors as described in <a href="#error-handling"></a>.
            </li>
            <li>
              If the previous step returned an error,
              <a>reject</a> <var>promise</var> with that error and abort these steps.
            </li>
            <li>
              <a>Queue a task</a> to perform the following steps:
              <ol>
                <li>
                  If <var>promise</var> is not in
                  <code>this.service.device.gatt.{{[[activeAlgorithms]]}}</code>,
                  <a>reject</a> <var>promise</var> with a {{NetworkError}} and abort these steps.
                </li>
                <li>
                  Let <var>buffer</var> be an {{ArrayBuffer}} holding the retrieved value,
                  and assign <code>new DataView(<var>buffer</var>)</code> to <code>this.value</code>.
                </li>
                <li>
                  <a>Fire an event</a> named {{characteristicvaluechanged}}
                  with its <code>bubbles</code> attribute initialized to <code>true</code>
                  at <code>this</code>.
                </li>
                <li>
                  <a>Resolve</a> <var>promise</var> with <code>this.value</code>.
                </li>
              </ol>
            </li>
          </ol>
        </li>
      </ol>
    </div>

    <div algorithm="Characteristic.writeValue()">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTCharacteristic">writeValue(<var>value</var>)</dfn></code> method, when invoked,
        MUST run the following steps:
      </p>
      <ol>
        <li>
          If <code>this.uuid</code> is <a>blocklisted for writes</a>,
          return <a>a promise rejected with</a> a {{SecurityError}}
          and abort these steps.
        </li>
        <li>
          Let <var>bytes</var> be
          <a>a copy of the bytes held</a> by <code><var>value</var></code>.
        </li>
        <li>
          If <var>bytes</var> is more than 512 bytes long
          (the maximum length of an attribute value, per <a>Long Attribute Values</a>)
          return <a>a promise rejected with</a> an {{InvalidModificationError}}
          and abort these steps.
        </li>
        <li>
          If <code>this.service.device.gatt.{{BluetoothRemoteGATTServer/connected}}</code> is `false`,
          return <a>a promise rejected with</a> a {{NetworkError}}
          and abort these steps.
        </li>
        <li>
          Let |characteristic| be
          <code>this.{{[[representedCharacteristic]]}}</code>.
        </li>
        <li>
          If |characteristic| is `null`,
          return <a>a promise rejected with</a> an {{InvalidStateError}} and
          abort these steps.
        </li>
        <li>
          Return a <code>this.service.device.gatt</code>-<a>connection-checking wrapper</a> around
          <a>a new promise</a> <var>promise</var>
          and run the following steps in parallel.
          <ol>
            <li>
              If none of the <code>Write</code>, <code>Write Without Response</code> or <code>Authenticated Signed Writes</code> bits are set
              in <var>characteristic</var>'s <a lt="Characteristic Properties">properties</a>,
              <a>reject</a> <var>promise</var> with a {{NotSupportedError}} and abort these steps.
            </li>
            <li>
              If the UA is currently using the Bluetooth system,
              it MAY <a>reject</a> |promise| with a {{NetworkError}}
              and abort these steps.

              Issue(188): Implementations may be able to avoid this {{NetworkError}},
              but for now sites need to serialize their use of this API
              and/or give the user a way to retry failed operations.
            </li>
            <li>
              Use any combination of the sub-procedures in
              the <a>Characteristic Value Write</a> procedure
              to write <var>bytes</var> to <var>characteristic</var>.
              Handle errors as described in <a href="#error-handling"></a>.
            </li>
            <li>
              If the previous step returned an error,
              <a>reject</a> <var>promise</var> with that error and abort these steps.
            </li>
            <li>
              <a>Queue a task</a> to perform the following steps:
              <ol>
                <li>
                  If <var>promise</var> is not in
                  <code>this.service.device.gatt.{{[[activeAlgorithms]]}}</code>,
                  <a>reject</a> <var>promise</var> with a {{NetworkError}} and abort these steps.
                </li>
                <li>
                  Set <code>this.value</code> to
                  a new {{DataView}} wrapping a new {{ArrayBuffer}} containing <var>bytes</var>.
                </li>
                <li>
                  <a>Resolve</a> <var>promise</var> with <code>undefined</code>.
                </li>
              </ol>
            </li>
          </ol>
        </li>
      </ol>
    </div>

    <p>
      The UA MUST maintain a map from each known GATT <a>Characteristic</a> to
      a set of {{Bluetooth}} objects known as
      the characteristic's <dfn>active notification context set</dfn>.
      <span class="note">
        The set for a given characteristic holds
        the {{Navigator/bluetooth|navigator.bluetooth}} objects for
        each <a>Realm</a> that has registered for notifications.
        All notifications become inactive when a device is disconnected.
        A site that wants to keep getting notifications after reconnecting
        needs to call {{startNotifications()}} again,
        and there is an unavoidable risk that
        some notifications will be missed
        in the gap before {{startNotifications()}} takes effect.
      </span>
    </p>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTCharacteristic">startNotifications()</dfn></code> method, when invoked,
        MUST return <a>a new promise</a> <var>promise</var>
        and run the following steps <a>in parallel</a>.
        See <a href="#notification-events"></a> for details of receiving notifications.
      </p>
      <ol>
        <li>
          If <code>this.uuid</code> is <a>blocklisted for reads</a>,
          <a>reject</a> <var>promise</var> with a {{SecurityError}} and abort these steps.
        </li>
        <li>
          If <code>this.service.device.gatt.{{BluetoothRemoteGATTServer/connected}}</code> is `false`,
          <a>reject</a> <var>promise</var> with a {{NetworkError}}
          and abort these steps.
        </li>
        <li>
          Let |characteristic| be
          <code>this.{{[[representedCharacteristic]]}}</code>.
        </li>
        <li>
          If |characteristic| is `null`,
          return <a>a promise rejected with</a> an {{InvalidStateError}} and
          abort these steps.
        </li>
        <li>
          If neither of the <code>Notify</code> or <code>Indicate</code> bits are set
          in <var>characteristic</var>'s <a lt="Characteristic Properties">properties</a>,
          <a>reject</a> <var>promise</var> with a {{NotSupportedError}} and abort these steps.
        </li>
        <li>
          If <var>characteristic</var>'s <a>active notification context set</a> contains
          {{Navigator/bluetooth|navigator.bluetooth}},
          <a>resolve</a> <var>promise</var> with `this` and abort these steps.
        </li>
        <li>
          If the UA is currently using the Bluetooth system,
          it MAY <a>reject</a> |promise| with a {{NetworkError}}
          and abort these steps.

          Issue(188): Implementations may be able to avoid this {{NetworkError}},
          but for now sites need to serialize their use of this API
          and/or give the user a way to retry failed operations.
        </li>
        <li>
          Use any of the <a>Characteristic Descriptors</a> procedures
          to ensure that one of the <code>Notification</code> or <code>Indication</code> bits in
          <var>characteristic</var>'s <a>Client Characteristic Configuration</a> descriptor
          is set, matching the constraints
          in <var>characteristic</var>'s <a lt="Characteristic Properties">properties</a>.
          The UA SHOULD avoid setting both bits,
          and MUST deduplicate <a href="#notification-events">value-change events</a>
          if both bits are set.
          Handle errors as described in <a href="#error-handling"></a>.
        </li>
        <li>
          If the previous step returned an error,
          <a>reject</a> <var>promise</var> with that error and abort these steps.
        </li>
        <li>
          Add {{Navigator/bluetooth|navigator.bluetooth}}
          to <var>characteristic</var>'s <a>active notification context set</a>.
        </li>
        <li>
          <a>Resolve</a> <var>promise</var> with `this`.
        </li>
      </ol>

      <p class="note">
        After notifications are enabled,
        the resulting <a href="#notification-events">value-change events</a> won't be delivered
        until after the current
        <a lt="perform a microtask checkpoint">microtask checkpoint</a>.
        This allows a developer to set up handlers
        in the <code>.then</code> handler of the result promise.
      </p>
    </div>

    <div algorithm>
      <p>
        The <code><dfn method for="BluetoothRemoteGATTCharacteristic">stopNotifications()</dfn></code> method, when invoked,
        MUST return <a>a new promise</a> <var>promise</var>
        and run the following steps <a>in parallel</a>:
      </p>
      <ol>
        <li>
          Let |characteristic| be
          <code>this.{{[[representedCharacteristic]]}}</code>.
        </li>
        <li>
          If |characteristic| is `null`,
          return <a>a promise rejected with</a> an {{InvalidStateError}} and
          abort these steps.
        </li>
        <li>
          If <var>characteristic</var>'s <a>active notification context set</a> contains
          {{Navigator/bluetooth|navigator.bluetooth}},
          remove it.
        </li>
        <li>
          If <var>characteristic</var>'s <a>active notification context set</a> became empty,
          the UA SHOULD use any of the <a>Characteristic Descriptors</a> procedures
          to clear the <code>Notification</code> and <code>Indication</code> bits in
          <var>characteristic</var>'s <a>Client Characteristic Configuration</a> descriptor.
        </li>
        <li>
          <a>Queue a task</a> to <a>resolve</a> <var>promise</var> with `this`.
        </li>
      </ol>

      <p class="note">
        Queuing a task to resolve the promise ensures that
        no <a href="#notification-events">value change events</a> due to notifications
        arrive after the promise resolves.
      </p>
    </div>

    <section>
      <h4 id="characteristicproperties"><dfn interface>BluetoothCharacteristicProperties</dfn></h4>

      <p>
        Each {{BluetoothRemoteGATTCharacteristic}} exposes its <a>characteristic properties</a>
        through a {{BluetoothCharacteristicProperties}} object.
        These properties express what operations are valid on the characteristic.
      </p>

      <pre class="idl">
        interface BluetoothCharacteristicProperties {
          readonly attribute boolean broadcast;
          readonly attribute boolean read;
          readonly attribute boolean writeWithoutResponse;
          readonly attribute boolean write;
          readonly attribute boolean notify;
          readonly attribute boolean indicate;
          readonly attribute boolean authenticatedSignedWrites;
          readonly attribute boolean reliableWrite;
          readonly attribute boolean writableAuxiliaries;
        };
      </pre>

      <div algorithm>
        <p>
          To <dfn>create a <code>BluetoothCharacteristicProperties</code> instance
          from the Characteristic</dfn> <var>characteristic</var>,
          the UA MUST return <a>a new promise</a> <var>promise</var>
          and run the following steps <a>in parallel</a>:
        </p>
        <ol>
          <li>
            Let <var>propertiesObj</var> be a new instance of {{BluetoothCharacteristicProperties}}.
          </li>
          <li>
            Let <var>properties</var> be
            the <a>characteristic properties</a> of <var>characteristic</var>.
          </li>
          <li>
            Initialize the attributes of <var>propertiesObj</var> from
            the corresponding bits in <var>properties</var>:
            <table class="data">
              <thead><th>Attribute</th><th>Bit</th></thead>
              <tr><td><code>broadcast</code></td><td>Broadcast</td></tr>
              <tr><td><code>read</code></td><td>Read</td></tr>
              <tr><td><code>writeWithoutResponse</code></td><td>Write Without Response</td></tr>
              <tr><td><code>write</code></td><td>Write</td></tr>
              <tr><td><code>notify</code></td><td>Notify</td></tr>
              <tr><td><code>indicate</code></td><td>Indicate</td></tr>
              <tr>
                <td><code>authenticatedSignedWrites</code></td>
                <td>Authenticated Signed Writes</td>
              </tr>
            </table>
          </li>
          <li>
            If the Extended Properties bit of the <a>characteristic properties</a> is not set,
            initialize <code><var>propertiesObj</var>.reliableWrite</code> and
            <code><var>propertiesObj</var>.writableAuxiliaries</code> to <code>false</code>.
            Otherwise, run the following steps:
            <ol>
              <li>
                <a lt="Characteristic Descriptor Discovery">Discover</a> the
                <a>Characteristic Extended Properties</a> descriptor for <var>characteristic</var>
                and <a lt="Read Characteristic Descriptors">read its value</a>
                into <var>extendedProperties</var>.
                Handle errors as described in <a href="#error-handling"></a>.
                <p class="issue">
                  <a>Characteristic Extended Properties</a> isn't clear whether
                  the extended properties are immutable for a given Characteristic.
                  If they are, the UA should be allowed to cache them.
                </p>
              </li>
              <li>
                If the previous step returned an error,
                <a>reject</a> <var>promise</var> with that error and abort these steps.
              </li>
              <li>
                Initialize <code><var>propertiesObj</var>.reliableWrite</code> from
                the Reliable Write bit of <var>extendedProperties</var>.
              </li>
              <li>
                Initialize <code><var>propertiesObj</var>.writableAuxiliaries</code> from
                the Writable Auxiliaries bit of <var>extendedProperties</var>.
              </li>
            </ol>
          </li>
          <li>
            <a>Resolve</a> <var>promise</var> with <var>propertiesObj</var>.
          </li>
        </ol>
      </div>
    </section>
  </section>

  <section class="unstable">
    <h3 oldids="bluetoothgattdescriptor"
        dfn-type="interface">BluetoothRemoteGATTDescriptor</h3>

    <p>{{BluetoothRemoteGATTDescriptor}} represents a GATT <a>Descriptor</a>, which provides further information about a <a>Characteristic</a>'s value.</p>

    <pre class="idl">
      interface BluetoothRemoteGATTDescriptor {
        [SameObject]
        readonly attribute BluetoothRemoteGATTCharacteristic characteristic;
        readonly attribute UUID uuid;
        readonly attribute DataView? value;
        Promise&lt;DataView> readValue();
        Promise&lt;void> writeValue(BufferSource value);
      };
    </pre>

    <div class="note" heading="{{BluetoothRemoteGATTDescriptor}} attributes"
         dfn-for="BluetoothRemoteGATTDescriptor" dfn-type="attribute">
      <p>
        <dfn>characteristic</dfn> is the GATT characteristic this descriptor belongs to.
      </p>
      <p>
        <dfn>uuid</dfn> is the UUID of the characteristic descriptor,
        e.g. <code>'00002902-0000-1000-8000-00805f9b34fb'</code> for the
        <a idl lt="org.bluetooth.descriptor.gatt.client_characteristic_configuration"
           >Client Characteristic Configuration</a> descriptor.
      </p>
      <p>
        <dfn>value</dfn> is the currently cached descriptor value.
        This value gets updated when the value of the descriptor is read.
      </p>
    </div>

    <p>
      Instances of {{BluetoothRemoteGATTDescriptor}} are created with the <a>internal slots</a>
      described in the following table:
    </p>
    <table class="data" dfn-for="BluetoothRemoteGATTDescriptor" dfn-type="attribute">
      <thead>
        <th><a>Internal Slot</a></th>
        <th>Initial Value</th>
        <th>Description (non-normative)</th>
      </thead>
      <tr>
        <td><dfn>\[[representedDescriptor]]</dfn></td>
        <td>&lt;always set in prose></td>
        <td>
          The <a>Descriptor</a> this object represents,
          or `null` if the Descriptor has been removed or otherwise invalidated.
        </td>
      </tr>
    </table>

    <div algorithm>
      <p>
        To <dfn>create a <code>BluetoothRemoteGATTDescriptor</code> representing</dfn>
        a Descriptor <var>descriptor</var>,
        the UA must return <a>a new promise</a> <var>promise</var>
        and run the following steps <a>in parallel</a>.
      </p>
      <ol>
        <li>
          Let <var>result</var> be a new instance of {{BluetoothRemoteGATTDescriptor}}
          with its {{[[representedDescriptor]]}} slot initialized to |descriptor|.
        </li>
        <li>
          Initialize <code><var>result</var>.characteristic</code> from
          the {{BluetoothRemoteGATTCharacteristic}} instance representing
          the Characteristic in which <var>descriptor</var> appears.
        </li>
        <li>
          Initialize <code><var>result</var>.uuid</code> from the UUID of <var>descriptor</var>.
        </li>
        <li>
          Initialize <code><var>result</var>.value</code> to <code>null</code>.
          The UA MAY initialize <code><var>result</var>.value</code> to
          a new {{DataView}} wrapping a new {{ArrayBuffer}} containing
          the most recently read value from <var>descriptor</var>
          if this value is available.
        </li>
        <li><a>Resolve</a> <var>promise</var> with <var>result</var>.</li>
      </ol>
    </div>

    <div algorithm="Descriptor.readValue()">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTDescriptor">readValue()</dfn></code> method, when invoked,
        MUST run the following steps:
      </p>
      <ol>
        <li>
          If <code>this.uuid</code> is <a>blocklisted for reads</a>,
          return <a>a promise rejected with</a> a {{SecurityError}}
          and abort these steps.
        </li>
        <li>
          If <code>this.characteristic.service.device.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
          is `false`,
          return <a>a promise rejected with</a> a {{NetworkError}}
          and abort these steps.
        </li>
        <li>
          Let |descriptor| be
          <code>this.{{[[representedDescriptor]]}}</code>.
        </li>
        <li>
          If |descriptor| is `null`,
          return <a>a promise rejected with</a> an {{InvalidStateError}} and
          abort these steps.
        </li>
        <li>
          Return a
          <code>this.characteristic.service.device.gatt</code>-<a>connection-checking wrapper</a> around
          <a>a new promise</a> <var>promise</var>
          and run the following steps <a>in parallel</a>:
          <ol>
            <li>
              If the UA is currently using the Bluetooth system,
              it MAY <a>reject</a> |promise| with a {{NetworkError}}
              and abort these steps.

              Issue(188): Implementations may be able to avoid this {{NetworkError}},
              but for now sites need to serialize their use of this API
              and/or give the user a way to retry failed operations.
            </li>
            <li>
              Use either the <a>Read Characteristic Descriptors</a> or
              the <a>Read Long Characteristic Descriptors</a> sub-procedure
              to retrieve the value of <var>descriptor</var>.
              Handle errors as described in <a href="#error-handling"></a>.
            </li>
            <li>
              If the previous step returned an error,
              <a>reject</a> <var>promise</var> with that error and abort these steps.
            </li>
            <li>
              <a>Queue a task</a> to perform the following steps:
              <ol>
                <li>
                  If <var>promise</var> is not in
                  <code>this.characteristic.service.device.gatt.{{[[activeAlgorithms]]}}</code>,
                  <a>reject</a> <var>promise</var> with a {{NetworkError}} and abort these steps.
                </li>
                <li>
                  Let <var>buffer</var> be an {{ArrayBuffer}} holding the retrieved value,
                  and assign <code>new DataView(<var>buffer</var>)</code> to <code>this.value</code>.
                </li>
                <li>
                  <a>Resolve</a> <var>promise</var> with <code>this.value</code>.
                </li>
              </ol>
            </li>
          </ol>
        </li>
      </ol>
    </div>

    <div algorithm="Descriptor.writeValue()">
      <p>
        The <code><dfn method for="BluetoothRemoteGATTDescriptor">writeValue(<var>value</var>)</dfn></code> method, when invoked,
        MUST  run the following steps:
      </p>
      <ol>
        <li>
          If <code>this.uuid</code> is <a>blocklisted for writes</a>,
          return <a>a promise rejected with</a> a {{SecurityError}}
          and abort these steps.
        </li>
        <li>
          Let <var>bytes</var> be
          <a>a copy of the bytes held</a> by <code><var>value</var></code>.
        </li>
        <li>
          If <var>bytes</var> is more than 512 bytes long
          (the maximum length of an attribute value, per <a>Long Attribute Values</a>)
          return <a>a promise rejected with</a> an {{InvalidModificationError}}
          and abort these steps.
        </li>
        <li>
          If <code>this.characteristic.service.device.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
          is `false`,
          return <a>a promise rejected with</a> a {{NetworkError}}
          and abort these steps.
        </li>
        <li>
          Let |descriptor| be
          <code>this.{{[[representedDescriptor]]}}</code>.
        </li>
        <li>
          If |descriptor| is `null`,
          return <a>a promise rejected with</a> an {{InvalidStateError}} and
          abort these steps.
        </li>
        <li>
          Return a
          <code>this.characteristic.service.device.gatt</code>-<a>connection-checking wrapper</a> around
          <a>a new promise</a> <var>promise</var>
          and run the following steps in parallel.
          <ol>
            <li>
              If the UA is currently using the Bluetooth system,
              it MAY <a>reject</a> |promise| with a {{NetworkError}}
              and abort these steps.

              Issue(188): Implementations may be able to avoid this {{NetworkError}},
              but for now sites need to serialize their use of this API
              and/or give the user a way to retry failed operations.
            </li>
            <li>
              Use either the <a>Write Characteristic Descriptors</a> or
              the <a>Write Long Characteristic Descriptors</a> sub-procedure
              to write <var>bytes</var> to <var>descriptor</var>.
              Handle errors as described in <a href="#error-handling"></a>.
            </li>
            <li>
              If the previous step returned an error,
              <a>reject</a> <var>promise</var> with that error and abort these steps.
            </li>
            <li>
              <a>Queue a task</a> to perform the following steps:
              <ol>
                <li>
                  If <var>promise</var> is not in
                  <code>this.characteristic.service.device.gatt.{{[[activeAlgorithms]]}}</code>,
                  <a>reject</a> <var>promise</var> with a {{NetworkError}} and abort these steps.
                </li>
                <li>
                  Set <code>this.value</code> to
                  a new {{DataView}} wrapping a new {{ArrayBuffer}} containing <var>bytes</var>.
                </li>
                <li>
                  <a>Resolve</a> <var>promise</var> with <code>undefined</code>.
                </li>
              </ol>
            </li>
          </ol>
        </li>
      </ol>
    </div>
  </section>

  <section>
    <h3 id="events">Events</h3>

    <section class="unstable">
      <h4 id="bluetooth-tree" dfn-type="dfn">Bluetooth Tree</h4>

      <p>
        {{Navigator/bluetooth|navigator.bluetooth}} and
        objects implementing the {{BluetoothDevice}}, {{BluetoothRemoteGATTService}},
        {{BluetoothRemoteGATTCharacteristic}}, or {{BluetoothRemoteGATTDescriptor}} interface
        <a>participate in a tree</a>,
        simply named the <a>Bluetooth tree</a>.

      <ul>
        <li>
          The <a>children</a>
          of {{Navigator/bluetooth|navigator.bluetooth}}</code></a>
          are the {{BluetoothDevice}} objects representing
          devices in the {{BluetoothPermissionData/allowedDevices}} list
          in {{"bluetooth"}}'s <a>extra permission data</a>
          for {{Navigator/bluetooth|navigator.bluetooth}}'s <a>relevant settings object</a>,
          in an unspecified order.
        </li>
        <li>
          The <a>children</a> of a {{BluetoothDevice}}
          are the {{BluetoothRemoteGATTService}} objects representing
          Primary and Secondary <a>Service</a>s on its <a>GATT Server</a>
          whose UUIDs are on the origin and device's
          {{AllowedBluetoothDevice/allowedServices}} list.
          The order of the primary services MUST be consistent with the order returned by
          the <a>Discover Primary Service by Service UUID</a> procedure,
          but secondary services and primary services with different UUIDs may be in any order.
        </li>
        <li>
          The <a>children</a> of a {{BluetoothRemoteGATTService}} are
          the {{BluetoothRemoteGATTCharacteristic}} objects representing its Characteristics.
          The order of the characteristics MUST be consistent with the order returned by
          the <a>Discover Characteristics by UUID</a> procedure,
          but characteristics with different UUIDs may be in any order.
        </li>
        <li>
          The <a>children</a> of a {{BluetoothRemoteGATTCharacteristic}} are
          the {{BluetoothRemoteGATTDescriptor}} objects representing its Descriptors
          in the order returned by the <a>Discover All Characteristic Descriptors</a> procedure.
        </li>
      </ul>
    </section>

    <section>
      <h4 id="event-types">Event types</h4>

      <dl>
        <dt><dfn event for="BluetoothDevice"><code>advertisementreceived</code></dfn></dt>
        <dd>
          Fired on a {{BluetoothDevice}}
          when an <a href="#advertising-event-algorithm">advertising event
          is received from that device</a>.
        </dd>

        <dt><dfn event for="Bluetooth"><code>availabilitychanged</code></dfn></dt>
        <dd>
          Fired on {{Bluetooth|navigator.bluetooth}} when
          <a href="#availability-changed-algorithm">the Bluetooth system as a whole
          becomes available or unavailable to the UA</a>.
        </dd>

        <dt><dfn event for="BluetoothRemoteGATTCharacteristic"><code>characteristicvaluechanged</code></dfn></dt>
        <dd>
          Fired on a {{BluetoothRemoteGATTCharacteristic}} when its value changes,
          either as a result of
          a <a idl for="BluetoothRemoteGATTCharacteristic" lt="readValue()">read request</a>,
          or a <a href="#notification-events">value change notification/indication</a>.
        </dd>

        <dt><dfn event for="BluetoothDevice"><code>gattserverdisconnected</code></dfn></dt>
        <dd>
          Fired on a {{BluetoothDevice}} when
          <a href="#disconnection-events">an active GATT connection is lost</a>.
        </dd>

        <dt><dfn event for="BluetoothRemoteGATTService"><code>serviceadded</code></dfn></dt>
        <dd>
          Fired on a new {{BluetoothRemoteGATTService}}
          <a href="#service-change-events">when it has been discovered on a remote device</a>,
          just after it is added to the <a>Bluetooth tree</a>.
        </dd>

        <dt><dfn event for="BluetoothRemoteGATTService"><code>servicechanged</code></dfn></dt>
        <dd>
          Fired on a {{BluetoothRemoteGATTService}}
          <a href="#service-change-events">when its state changes</a>.
          This involves any characteristics and/or descriptors
          that get added or removed from the service,
          as well as <a>Service Changed</a> indications from the remote device.
        </dd>

        <dt><dfn event for="BluetoothRemoteGATTService"><code>serviceremoved</code></dfn></dt>
        <dd>
          Fired on a {{BluetoothRemoteGATTService}}
          <a href="#service-change-events">when it has been removed from its device</a>,
          just before it is removed from the <a>Bluetooth tree</a>.
        </dd>

      </dl>
    </section>

    <section>
      <h4 id="disconnection-events">Responding to Disconnection</h4>

      <div algorithm="disconnection">
        <p>
          When a <a>Bluetooth device</a> <var>device</var>'s <a>ATT Bearer</a> is lost
          (e.g. because the remote device moved out of range
          or the user used a platform feature to disconnect it),
          for each {{BluetoothDevice}} <var>deviceObj</var> the UA MUST
          <a>queue a task</a> on <var>deviceObj</var>'s <a>relevant settings object</a>'s
          <a>responsible event loop</a>
          to perform the following steps:
        </p>
        <ol>
          <li>
            If <code><var>deviceObj</var>.{{BluetoothDevice/[[representedDevice]]}}</code>
            is not the <a>same device</a> as <var>device</var>,
            abort these steps.
          </li>
          <li>
            If <code>!<var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>,
            abort these steps.
          </li>
          <li>
            <a>Clean up the disconnected device</a> |deviceObj|.
          </li>
        </ol>
      </div>

      <div algorithm>
        <p>
          To <dfn>clean up the disconnected device</dfn> |deviceObj|, the UA must:
        </p>
        <ol>
          <li>
            Set <code><var>deviceObj</var>.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
            to `false`.
          </li>
          <li>
            Clear <code><var>deviceObj</var>.gatt.{{[[activeAlgorithms]]}}</code>.
          </li>
          <li>
            Let |context| be <code>|deviceObj|.{{[[context]]}}</code>.
          </li>
          <li>
            Remove all entries from <code>|context|.{{[[attributeInstanceMap]]}}</code>
            whose keys are inside <code>|deviceObj|.{{[[representedDevice]]}}</code>.
          </li>
          <li>
            For each {{BluetoothRemoteGATTService}} |service|
            in |deviceObj|'s <a>realm</a>,
            set <code>|service|.{{[[representedService]]}}</code> to `null`.
          </li>
          <li>
            For each {{BluetoothRemoteGATTCharacteristic}} |characteristic|
            in |deviceObj|'s <a>realm</a>, do the following sub-steps:
            <ol>
              <li>
                Let |notificationContexts| be
                <code>|characteristic|.{{[[representedCharacteristic]]}}</code>'s
                <a>active notification context set</a>.
              <li>
                Remove |context| from |notificationContexts|.
              </li>
              <li>
                If |notificationContexts| became empty
                and there is still an <a>ATT Bearer</a>
                to <code>|deviceObj|.{{[[representedDevice]]}}</code>,
                the UA SHOULD use any of the <a>Characteristic Descriptors</a> procedures
                to clear the <code>Notification</code> and <code>Indication</code> bits in
                <var>characteristic</var>'s <a>Client Characteristic Configuration</a> descriptor.
              </li>
              <li>
                Set <code>|characteristic|.{{[[representedCharacteristic]]}}</code>
                to `null`.
              </li>
            </ol>
          </li>
          <li>
            For each {{BluetoothRemoteGATTDescriptor}} |descriptor|
            in |deviceObj|'s <a>realm</a>,
            set <code>|descriptor|.{{[[representedDescriptor]]}}</code> to `null`.
          </li>
          <li>
            <a>Fire an event</a> named {{gattserverdisconnected}}
            with its {{Event/bubbles}} attribute initialized to `true`
            at <code><var>deviceObj</var></code>.
            <p class="note">
              This event is <em>not</em> fired at the {{BluetoothRemoteGATTServer}}.
            </p>
          </li>
        </ol>
      </div>
    </section>

    <section>
      <h4 id="notification-events">Responding to Notifications and Indications</h4>

      <div algorithm="notifications">
        <p>
          When the UA receives a Bluetooth <a>Characteristic Value Notification</a>
          or <a lt="Characteristic Value Indications">Indication</a>,
          it must perform the following steps:
        </p>
        <ol>
          <li>
            For each <var>bluetoothGlobal</var> in
            the Characteristic's <a>active notification context set</a>,
            <a>queue a task</a>
            on the event loop of the script settings object of <var>bluetoothGlobal</var>
            to do the following sub-steps:

            <ol>
              <li>
                Let <var>characteristicObject</var> be the {{BluetoothRemoteGATTCharacteristic}} in
                the <a>Bluetooth tree</a> rooted at <var>bluetoothGlobal</var>
                that represents the <a>Characteristic</a>.
              </li>
              <li>
                If <code><var>characteristicObject</var>.service.device.gatt.{{BluetoothRemoteGATTServer/connected}}</code>
                is `false`,
                abort these sub-steps.
              </li>
              <li>
                Set <code><var>characteristicObject</var>.value</code> to
                a new {{DataView}} wrapping a new {{ArrayBuffer}} holding
                the new value of the <a>Characteristic</a>.
              </li>
              <li>
                <a>Fire an event</a> named {{characteristicvaluechanged}}
                with its <code>bubbles</code> attribute initialized to <code>true</code>
                at <var>characteristicObject</var>.
              </li>
            </ol>
          </li>
        </ol>
      </div>
    </section>

    <section class="unstable">
      <h4 id="service-change-events">Responding to Service Changes</h4>

      <div algorithm="service changes">
        <p>
          The Bluetooth <a>Attribute Caching</a> system allows clients
          to track changes to <a>Service</a>s, <a>Characteristic</a>s, and <a>Descriptor</a>s.
          Before discovering any of these attributes for the purpose of exposing them to a web page
          the UA MUST subscribe to Indications from the
          <a>Service Changed</a> characteristic, if it exists.
          When the UA receives an Indication on the Service Changed characteristic,
          it MUST perform the following steps.
        </p>
        <ol>
          <li>
            Let <var>removedAttributes</var> be the list of attributes in
            the range indicated by the Service Changed characteristic
            that the UA had discovered before the Indication.
          </li>
          <li>
            Use the <a>Primary Service Discovery</a>, <a>Relationship Discovery</a>, <a>Characteristic Discovery</a>,
            and <a>Characteristic Descriptor Discovery</a> procedures
            to re-discover attributes in the range indicated by the Service Changed characteristic.
            The UA MAY skip discovering all or part of the indicated range
            if it can prove that the results of that discovery
            could not affect the events fired below.
          </li>
          <li>
            Let <var>addedAttributes</var> be the list of attributes discovered in the previous step.
          </li>
          <li>
            If an attribute with the same definition
            (see the <a>Service Interoperability Requirements</a>),
            ignoring Characteristic and Descriptor values,
            appears in both <var>removedAttributes</var> and <var>addedAttributes</var>,
            remove it from both.

            <div class="example" id="example-changed-service">
              <p>
                Given the following device states:
              </p>

              <dl>
                <dt>State 1</dt>
                <dd>
                  <ul>
                    <li>Service A
                      <ul>
                        <li>Characteristic C: value `[1, 2, 3]`</li>
                      </ul>
                    </li>
                    <li>Service B</li>
                  </ul>
                </dd>

                <dt>State 2</dt>
                <dd>
                  <ul>
                    <li>Service A
                      <ul>
                        <li>Characteristic C: value `[3, 2, 1]`</li>
                      </ul>
                    </li>
                    <li>Service B</li>
                  </ul>
                </dd>

                <dt>State 3</dt>
                <dd>
                  <ul>
                    <li>Service A
                      <ul>
                        <li>Characteristic D: value `[3, 2, 1]`</li>
                      </ul>
                    </li>
                    <li>Service B</li>
                  </ul>
                </dd>

                <dt>State 4</dt>
                <dd>
                  <ul>
                    <li>Service A
                      <ul>
                        <li>Characteristic C: value `[1, 2, 3]`</li>
                      </ul>
                    </li>
                    <li>Service B
                      <ul>
                        <li>Include Service A</li>
                      </ul>
                    </li>
                  </ul>
                </dd>
              </dl>

              <p>
                A transition from state 1 to 2 leaves service A with
                "the same definition, ignoring Characteristic and Descriptor values",
                which means it's removed from both |removedAttributes| and |addedAttributes|,
                and it wouldn't appear in any {{servicechanged}} events.
              </p>
              <p>
                A transition from state 1 to 3 leaves service A with a different definition,
                because a <a>service definition</a> includes its characteristic definitions,
                so it's left in both |removedAttributes| and |addedAttributes|.
                Then in <a href="#same-service-removed-and-added">step 8</a>,
                the service is moved to |changedServices|,
                which makes it cause a {{servicechanged}} event
                instead of both a {{serviceadded}} and {{serviceremoved}}.
                <a href="#characteristic-descriptor-change-adds-changed-service">Step 9</a>
                also adds service A to |changedServices| because
                characteristic C was removed and characteristic D was added.
              </p>
              <p>
                A transition from state 1 to 4 is similar to the 1->3 transition.
                Service B is moved to |changedServices| in
                <a href="#same-service-removed-and-added">step 8</a>,
                but no characteristics or descriptors have changed,
                so it's not redundantly added in
                <a href="#characteristic-descriptor-change-adds-changed-service">step 9</a>.
              </p>
            </div>
          </li>
          <li>
            Let |invalidatedAttributes| be
            the attributes in |removedAttributes| but not |addedAttributes|.
          </li>
          <li>
            For each <a>environment settings object</a> |settings| in the UA,
            <a>queue a task</a> on its <a>responsible event loop</a> to
            do the following sub-steps:
            <ol>
              <li>
                For each {{BluetoothRemoteGATTService}} |service|
                whose <a>relevant settings object</a> is |settings|,
                if <code>|service|.{{[[representedService]]}}</code>
                is in |invalidatedAttributes|,
                set <code>|service|.{{[[representedService]]}}</code> to `null`.
              </li>
              <li>
                For each {{BluetoothRemoteGATTCharacteristic}} |characteristic|
                whose <a>relevant settings object</a> is |settings|,
                if <code>|characteristic|.{{[[representedCharacteristic]]}}</code>
                is in |invalidatedAttributes|,
                set <code>|characteristic|.{{[[representedCharacteristic]]}}</code> to `null`.
              </li>
              <li>
                For each {{BluetoothRemoteGATTDescriptor}} |descriptor|
                whose <a>relevant settings object</a> is |settings|,
                if <code>|descriptor|.{{[[representedDescriptor]]}}</code>
                is in |invalidatedAttributes|,
                set <code>|descriptor|.{{[[representedDescriptor]]}}</code> to `null`.
              </li>
              <li>
                Let |global| be |settings|'
                <a for="environment settings object">global object</a>.
              </li>
              <li>
                Remove every entry
                from <code>|global|.navigator.bluetooth.{{[[attributeInstanceMap]]}}</code>
                that represents an attribute that
                is in |invalidatedAttributes|.
              </li>
            </ol>
          </li>
          <li>
            Let <var>changedServices</var> be a set of <a>Service</a>s, initially empty.
          </li>
          <li id="same-service-removed-and-added">
            If the <a lt="same attribute">same</a> <a>Service</a> appears in
            both <var>removedAttributes</var> and <var>addedAttributes</var>,
            remove it from both, and add it to <var>changedServices</var>.
          </li>
          <li id="characteristic-descriptor-change-adds-changed-service">
            For each <a>Characteristic</a> and <a>Descriptor</a>
            in <var>removedAttributes</var> or <var>addedAttributes</var>,
            remove it from its original list,
            and add its parent <a>Service</a> to <var>changedServices</var>.
            <span class="note">After this point, <var>removedAttributes</var> and <var>addedAttributes</var> contain only <a>Service</a>s.</span>
          </li>
          <li id="only-notify-for-requested-services">
            If a <a>Service</a> in <var>addedAttributes</var>
            would not have been returned from any previous call to
            <code>getPrimaryService</code>, <code>getPrimaryServices</code>,
            <code>getIncludedService</code>, or <code>getIncludedServices</code>
            if it had existed at the time of the call,
            the UA MAY remove the <a>Service</a> from <var>addedAttributes</var>.
          </li>
          <li>
            Let <var>changedDevices</var> be the set of <a>Bluetooth device</a>s that
            contain any <a>Service</a> in
            <var>removedAttributes</var>, <var>addedAttributes</var>, and <var>changedServices</var>.
          </li>
          <li>
            For each {{BluetoothDevice}} <var>deviceObj</var>
            that is connected to a device in <var>changedDevices</var>,
            <a>queue a task</a> on its <a>relevant global object</a>'s
            <a>responsible event loop</a>
            to do the following steps:
            <ol>
              <li>
                For each <a>Service</a> <var>service</var> in <var>removedAttributes</var>:
                <ol>
                  <li>
                    If <code><var>deviceObj</var>.{{BluetoothDevice/[[allowedServices]]}}</code>
                    is `"all"` or contains the Service's UUID,
                    <a>fire an event</a> named {{serviceremoved}}
                    with its <code>bubbles</code> attribute initialized to <code>true</code>
                    at the {{BluetoothRemoteGATTService}} representing the <a>Service</a>.
                  </li>
                  <li>
                    Remove this {{BluetoothRemoteGATTService}} from the <a>Bluetooth tree</a>.
                  </li>
                </ol>
              </li>
              <li>
                For each <a>Service</a> in <var>addedAttributes</var>,
                if <code>deviceObj.{{BluetoothDevice/[[allowedServices]]}}</code>
                is `"all"` or contains the Service's UUID,
                add the {{BluetoothRemoteGATTService}} representing this <a>Service</a> to the <a>Bluetooth tree</a>
                and then <a>fire an event</a> named {{serviceadded}}
                with its <code>bubbles</code> attribute initialized to <code>true</code>
                at the {{BluetoothRemoteGATTService}}.
              </li>
              <li>
                For each <a>Service</a> in <var>changedServices</var>,
                if <code>deviceObj.{{BluetoothDevice/[[allowedServices]]}}</code>
                is `"all"` or contains the Service's UUID,
                <a>fire an event</a> named {{servicechanged}}
                with its <code>bubbles</code> attribute initialized to <code>true</code>
                at the {{BluetoothRemoteGATTService}} representing the <a>Service</a>.
              </li>
            </ol>
          </li>
        </ol>
      </div>
    </section>

    <section>
      <h4 id="idl-event-handlers">IDL event handlers</h4>

      <pre class="idl">
        [NoInterfaceObject]
        interface CharacteristicEventHandlers {
          attribute EventHandler oncharacteristicvaluechanged;
        };
      </pre>
      <p>
        <dfn attribute for="CharacteristicEventHandlers">oncharacteristicvaluechanged</dfn>
        is an <a>Event handler IDL attribute</a>
        for the {{characteristicvaluechanged}} event type.
      </p>

      <pre class="idl">
        [NoInterfaceObject]
        interface BluetoothDeviceEventHandlers {
          attribute EventHandler ongattserverdisconnected;
        };
      </pre>
      <p>
        <dfn attribute for="BluetoothDeviceEventHandlers">ongattserverdisconnected</dfn>
        is an <a>Event handler IDL attribute</a>
        for the {{gattserverdisconnected}} event type.
      </p>

      <pre class="idl">
        [NoInterfaceObject]
        interface ServiceEventHandlers {
          attribute EventHandler onserviceadded;
          attribute EventHandler onservicechanged;
          attribute EventHandler onserviceremoved;
        };
      </pre>
      <p>
        <dfn attribute for="ServiceEventHandlers">onserviceadded</dfn>
        is an <a>Event handler IDL attribute</a>
        for the {{serviceadded}} event type.
      </p>
      <p>
        <dfn attribute for="ServiceEventHandlers">onservicechanged</dfn>
        is an <a>Event handler IDL attribute</a>
        for the {{servicechanged}} event type.
      </p>
      <p>
        <dfn attribute for="ServiceEventHandlers">onserviceremoved</dfn>
        is an <a>Event handler IDL attribute</a>
        for the {{serviceremoved}} event type.
      </p>
    </section>
  </section>

  <section>
    <h3 id="error-handling">Error handling</h3>

    <p class="note">
      This section primarily defines the mapping from system errors to Javascript error names
      and allows UAs to retry certain operations.
      The retry logic and possible error distinctions
      are highly constrained by the operating system,
      so places these requirements don't reflect reality
      are likely <a href="https://github.com/WebBluetoothCG/web-bluetooth/issues">spec bugs</a>
      instead of browser bugs.
    </p>

    <div algorithm="error handling">
      <p>
        When the UA is using a <a>GATT procedure</a>
        to execute a step in an algorithm or to handle a query to the <a>Bluetooth cache</a>
        (both referred to as a "step", here),
        and the GATT procedure returns an <code><a>Error Response</a></code>,
        the UA MUST perform the following steps:
      </p>

      <ol>
        <li>
          If the <a>procedure times out</a> or
          the ATT Bearer (described in <a>Profile Fundamentals</a>) is
          absent or terminated for any reason,
          return a {{NetworkError}} from the step and abort these steps.
        </li>
        <li>
          Take the following actions depending on the <code>Error Code</code>:
          <dl class="switch">
            <dt><code>Invalid PDU</code></dt>
            <dt><code>Invalid Offset</code></dt>
            <dt><code>Attribute Not Found</code></dt>
            <dt><code>Unsupported Group Type</code></dt>
            <dd>
              These error codes indicate that something unexpected happened at the protocol layer,
              likely either due to a UA or device bug.
              Return a {{NotSupportedError}} from the step.
            </dd>

            <dt><code>Invalid Handle</code></dt>
            <dd>
              Return an {{InvalidStateError}} from the step.
            </dd>

            <dt><code>Invalid Attribute Value Length</code></dt>
            <dd>
              Return an {{InvalidModificationError}} from the step.
            </dd>

            <dt><code>Attribute Not Long</code></dt>
            <dd>
              <p>
                If this error code is received without having used a "Long" sub-procedure,
                this may indicate a device bug.
                Return a {{NotSupportedError}} from the step.
              </p>

              <p>
                Otherwise, retry the step without using a "Long" sub-procedure.
                If this is impossible due to the length of the value being written,
                return an {{InvalidModificationError}} from the step.
              </p>
            </dd>

            <dt><code>Insufficient Authentication</code></dt>
            <dt><code>Insufficient Encryption</code></dt>
            <dt><code>Insufficient Encryption Key Size</code></dt>
            <dd>
              The UA SHOULD attempt to increase the security level of the connection.
              If this attempt fails or the UA doesn't support any higher security,
              Return a {{SecurityError}} from the step.
              Otherwise, retry the step at the new higher security level.
            </dd>

            <dt><code>Insufficient Authorization</code></dt>
            <dd>
              Return a {{SecurityError}} from the step.
            </dd>

            <dt><code>Application Error</code></dt>
            <dd>
              If the GATT procedure was a Write,
              return an {{InvalidModificationError}} from the step.
              Otherwise, return a {{NotSupportedError}} from the step.
            </dd>

            <dt><code>Read Not Permitted</code></dt>
            <dt><code>Write Not Permitted</code></dt>
            <dt><code>Request Not Supported</code></dt>
            <dt><code>Prepare Queue Full</code></dt>
            <dt><code>Insufficient Resources</code></dt>
            <dt><code>Unlikely Error</code></dt>
            <dt>Anything else</dt>
            <dd>
              Return a {{NotSupportedError}} from the step.
            </dd>
          </dl>
        </li>
      </ol>
    </div>
  </section>
</section>

<section>
  <h2 id="uuids">UUIDs</h2>

  <pre class="idl">typedef DOMString UUID;</pre>
  <p>
    A <a>UUID</a> string represents a 128-bit [[!RFC4122]] UUID.
    A <dfn>valid UUID</dfn> is a string that matches
    the [[!ECMAScript]] regexp
    <code>/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/</code>.
    That is, a <a>valid UUID</a> is lower-case and
    does not use the 16- or 32-bit abbreviations defined by the Bluetooth standard.
    All UUIDs returned from functions and attributes in this specification
    MUST be <a>valid UUID</a>s.
    If a function in this specification takes a parameter whose type is <a>UUID</a>
    or a dictionary including a <a>UUID</a> attribute,
    and the argument passed in any <a>UUID</a> slot is not a <a>valid UUID</a>,
    the function MUST return <a>a promise rejected with</a> a <code>TypeError</code>
    and abort its other steps.
  </p>
  <p class="note">
    This standard provides the
    <code>BluetoothUUID.<a for="BluetoothUUID">canonicalUUID(<var>alias</var>)</a></code> function
    to map a 16- or 32-bit Bluetooth <a>UUID alias</a> to its 128-bit form.
  </p>
  <p class="note">
    Bluetooth devices are required to convert 16- and 32-bit UUIDs to 128-bit UUIDs
    before comparing them (as described in <a>Attribute Type</a>), but not all devices do so.
    To interoperate with these devices,
    if the UA has received a UUID from the device in one form (16-, 32-, or 128-bit),
    it should send other aliases of that UUID back to the device in the same form.
  </p>

  <section>
    <h3 id="standardized-uuids">Standardized UUIDs</h3>

    <p>
      The Bluetooth SIG maintains a registry at [[BLUETOOTH-ASSIGNED]] of
      UUIDs that identify services, characteristics, descriptors, and other entities.
      This section provides a way for script to look up those UUIDs by name
      so they don't need to be replicated in each application.
    </p>

    <pre class="idl">
      interface BluetoothUUID {
        static UUID getService((DOMString or unsigned long) name);
        static UUID getCharacteristic((DOMString or unsigned long) name);
        static UUID getDescriptor((DOMString or unsigned long) name);

        static UUID canonicalUUID([EnforceRange] unsigned long alias);
      };

      typedef (DOMString or unsigned long) BluetoothServiceUUID;
      typedef (DOMString or unsigned long) BluetoothCharacteristicUUID;
      typedef (DOMString or unsigned long) BluetoothDescriptorUUID;
    </pre>

    <p>
      The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">canonicalUUID(<var>alias</var>)</dfn></code> method, when invoked,
      MUST return <a>the 128-bit UUID represented</a>
      by the 16- or 32-bit UUID alias <var>alias</var>.
    </p>
    <p class="note">
      This algorithm consists of
      replacing the top 32 bits of "<code>00000000-0000-1000-8000-00805f9b34fb</code>"
      with the bits of the alias.
      For example, <code>canonicalUUID(0xDEADBEEF)</code> returns
      <code>"deadbeef-0000-1000-8000-00805f9b34fb"</code>.
    </p>

    <div class="note">
      <p>
        <dfn typedef>BluetoothServiceUUID</dfn> represents
        16- and 32-bit UUID aliases, <a>valid UUID</a>s,
        and names defined in [[BLUETOOTH-ASSIGNED-SERVICES]],
        or, equivalently, the values for which
        {{BluetoothUUID/getService()|BluetoothUUID.getService()}} does not throw an exception.
      </p>
      <p>
        <dfn typedef>BluetoothCharacteristicUUID</dfn> represents
        16- and 32-bit UUID aliases, <a>valid UUID</a>s,
        and names defined in [[BLUETOOTH-ASSIGNED-CHARACTERISTICS]],
        or, equivalently, the values for which
        {{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic()}} does not throw an exception.
      </p>
      <p>
        <dfn typedef>BluetoothDescriptorUUID</dfn> represents
        16- and 32-bit UUID aliases, <a>valid UUID</a>s,
        and names defined in [[BLUETOOTH-ASSIGNED-DESCRIPTORS]],
        or, equivalently, the values for which
        {{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor()}} does not throw an exception.
      </p>
    </div>

    <div algorithm>
      <p>
        To <dfn>ResolveUUIDName</dfn>(<var>name</var>,
        <var>assigned numbers table</var>, <var>prefix</var>),
        the UA MUST perform the following steps:
      </p>
      <ol>
        <li>
          If <var>name</var> is an <code>unsigned long</code>,
          return {{BluetoothUUID/canonicalUUID()|BluetoothUUID.canonicalUUID}}(name)</code> and abort these steps.
        </li>
        <li>
          If <var>name</var> is a <a>valid UUID</a>,
          return <var>name</var> and abort these steps.
        </li>
        <li>
          If the string <code><var>prefix</var> + "." + <var>name</var></code>
          appears in <var>assigned numbers table</var>,
          let <var>alias</var> be its assigned number,
          and return {{BluetoothUUID/canonicalUUID()|BluetoothUUID.canonicalUUID}}(<var>alias</var>)</code>.
        </li>
        <li>
          Otherwise, throw a {{TypeError}}.
        </li>
      </ol>
    </div>

    <p>
      The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">getService(<var>name</var>)</dfn></code> method, when invoked,
      MUST return <a>ResolveUUIDName</a>(<code><var>name</var></code>,
      [[!BLUETOOTH-ASSIGNED-SERVICES]], "org.bluetooth.service").
    </p>
    <p>
      The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">getCharacteristic(<var>name</var>)</dfn></code> method, when invoked,
      MUST return <a>ResolveUUIDName</a>(<code><var>name</var></code>,
      [[!BLUETOOTH-ASSIGNED-CHARACTERISTICS]], "org.bluetooth.characteristic").
    </p>
    <p>
      The static <code>BluetoothUUID.<dfn method for="BluetoothUUID">getDescriptor(<var>name</var>)</dfn></code> method, when invoked,
      MUST return <a>ResolveUUIDName</a>(<code><var>name</var></code>,
      [[!BLUETOOTH-ASSIGNED-DESCRIPTORS]], "org.bluetooth.descriptor").
    </p>

    <div class="example">
      <p>
        <code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}("<a idl lt="org.bluetooth.service.cycling_power"
          >cycling_power</a>")</code>
        returns <code>"00001818-0000-1000-8000-00805f9b34fb"</code>.
      </p>
      <p>
        <code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}("00001801-0000-1000-8000-00805f9b34fb")</code>
        returns <code>"00001801-0000-1000-8000-00805f9b34fb"</code>.
      </p>
      <p>
        <code>{{BluetoothUUID/getService()|BluetoothUUID.getService}}("unknown-service")</code>
        throws a {{TypeError}}.
      </p>
      <p>
        <code>{{BluetoothUUID/getCharacteristic()|BluetoothUUID.getCharacteristic}}("{{org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list|ieee_11073-20601_regulatory_certification_data_list}}")</code>
        returns <code>"00002a2a-0000-1000-8000-00805f9b34fb"</code>.
      </p>
      <p>
        <code>{{BluetoothUUID/getDescriptor()|BluetoothUUID.getDescriptor}}("{{org.bluetooth.descriptor.gatt.characteristic_presentation_format|gatt.characteristic_presentation_format}}")</code>
        returns <code>"00002904-0000-1000-8000-00805f9b34fb"</code>.
      </p>
    </div>
  </section>
</section>

<section>
  <h2 id="the-gatt-blocklist">The GATT Blocklist</h2>

  <p>
    This specification relies on a blocklist file in the
    <a href="https://github.com/WebBluetoothCG/registries"
       >https://github.com/WebBluetoothCG/registries</a> repository
    to restrict the set of GATT attributes a website can access.
  </p>

  <div algorithm>
    <p>
      The result of <dfn>parsing the blocklist</dfn> at a URL <var>url</var>
      is a map from <a>valid UUID</a>s to tokens, or an error,
      produced by the following algorithm:
    </p>
    <ol>
      <li>Fetch <var>url</var>, and let <var>contents</var> be its body, decoded as UTF-8.</li>
      <li>Let <var>lines</var> be <var>contents</var> split on <code>'\n'</code>.</li>
      <li>
        Let <var>result</var> be an empty map.
      </li>
      <li>
        For each <var>line</var> in <var>lines</var>, do the following sub-steps:
        <ol>
          <li>
            If <var>line</var> is empty or its first character is <code>'#'</code>,
            continue to the next line.
          </li>
          <li>
            If <var>line</var> consists of just a <a>valid UUID</a>,
            let <var>uuid</var> be that UUID and
            let <var>token</var> be "<code>exclude</code>".
          </li>
          <li>
            If <var>line</var> consists of a <a>valid UUID</a>, a space (U+0020),
            and one of the tokens "<code>exclude-reads</code>" or "<code>exclude-writes</code>",
            let <var>uuid</var> be that UUID and
            let <var>token</var> be that token.
          </li>
          <li>
            Otherwise, return an error and abort these steps.
          </li>
          <li>
            If <var>uuid</var> is already in <var>result</var>,
            return an error and abort these steps.
          </li>
          <li>
            Add a mapping in <var>result</var> from <var>uuid</var> to <var>token</var>.
          </li>
        </ol>
      </li>
      <li>Return <var>result</var>.</li>
    </ol>
  </div>

  <p>
    The <dfn>GATT blocklist</dfn> is the result of <a>parsing the blocklist</a> at
    <a href="https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt"
       >https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt</a>.
    The UA should re-fetch the blocklist periodically, but it's unspecified how often.
  </p>

  <p>
    A <a>UUID</a> is <dfn>blocklisted</dfn> if either
    the <a>GATT blocklist</a>'s value is an error,
    or the UUID maps to "<code>exclude</code>" in the <a>GATT blocklist</a>.
  </p>
  <p>
    A <a>UUID</a> is <dfn>blocklisted for reads</dfn> if either
    the <a>GATT blocklist</a>'s value is an error,
    or the UUID maps to either "<code>exclude</code>" or "<code>exclude-reads</code>"
    in the <a>GATT blocklist</a>.
  </p>
  <p>
    A <a>UUID</a> is <dfn>blocklisted for writes</dfn> if either
    the <a>GATT blocklist</a>'s value is an error,
    or the UUID maps to either "<code>exclude</code>" or "<code>exclude-writes</code>"
    in the <a>GATT blocklist</a>.
  </p>
</section>

<section>
  <h2 id="navigator-extensions">Extensions to the Navigator Interface</h2>

  <pre class="idl">
    partial interface Navigator {
      [SameObject]
      readonly attribute Bluetooth bluetooth;
    };
  </pre>
</section>

<section>
  <h2 id="terminology">Terminology and Conventions</h2>

  <p>
    This specification uses a few conventions and several terms from other specifications.
    This section lists those and links to their primary definitions.
  </p>

  <p>
    When an algorithm in this specification uses a name defined in this or another specification,
    the name MUST resolve to its initial value,
    ignoring any changes that have been made to the name in the current execution environment.
    For example, when the {{Bluetooth/requestDevice()}} algorithm says to call
    <code>{{Array.prototype.map}}.call(<var>filter</var>.services,
      {{BluetoothUUID/getService()|BluetoothUUID.getService}})</code>,
    this MUST apply the
    {{Array.prototype.map}} algorithm defined in [[ECMAScript]]
    with <code><var>filter</var>.services</code> as its <code>this</code> parameter and
    the algorithm defined in <a href="#standardized-uuids"></a>
    for {{BluetoothUUID/getService()|BluetoothUUID.getService}}
    as its <code>callbackfn</code> parameter,
    regardless of any modifications that have been made to <code>window</code>,
    <code>Array</code>, <code>Array.prototype</code>, <code>Array.prototype.map</code>,
    <code>Function</code>, <code>Function.prototype</code>, <code>BluetoothUUID</code>,
    <code>BluetoothUUID.getService</code>, or other objects.
  </p>

  <p>
    This specification uses a read-only type
    that is similar to WebIDL's {{FrozenArray}}.
  </p>
  <ul>
    <li>
      A <dfn>read only ArrayBuffer</dfn> has {{ArrayBuffer}}'s values and interface,
      except that attempting to write to its contents or transfer it
      has the same effect as trying to write to a {{FrozenArray}}'s contents.
      This applies to {{TypedArray}}s and {{DataView}}s
      wrapped around the {{ArrayBuffer}} too.
    </li>
  </ul>

  <dl>
    <dt>[[!BLUETOOTH42]]</dt>
    <dd>
      <ol>
        <li value="1">Architecture &amp; Terminology Overview
          <ol type="A">
            <li value="1">General Description
              <ol>
                <li value="2">Overview of Bluetooth Low Energy Operation
                  (defines <dfn lt="advertising event|advertising events">advertising events</dfn>)
                </li>
              </ol>
            </li>
            <li value="4">Communication Topology and Operation
              <ol>
                <li value="2">Operational Procedures and Modes
                  <ol>
                    <li value="1">BR/EDR Procedures
                      <ol>
                        <li value="1">Inquiry (Discovering) Procedure
                          <ol>
                            <li value="1"><dfn>Extended Inquiry Response</dfn></li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
              </ol>
            </li>
          </ol>
        </li>
        <li value="2">Core System Package [BR/EDR Controller volume]
          <ol type="A">
            <li value="5">Host Controller Interface Functional Specification
              <ol>
                <li value="7">HCI Commands and Events
                  <ol>
                    <li value="4">Informational Parameters
                      <ol>
                        <li value="6"><dfn>Read BD_ADDR Command</dfn></li>
                      </ol>
                    </li>
                    <li value="5">Status Parameters
                      <ol>
                        <li value="4">
                          Read <dfn lt="RSSI|Received Signal Strength"
                                    ><abbr title="Received Signal Strength Indication"
                                           >RSSI</abbr></dfn> Command
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
              </ol>
            </li>
          </ol>
        </li>
        <li value="3">Core System Package [Host volume]
          <ol type="A">
            <li value="2">Service Discovery Protocol (SDP) Specification
              <ol>
                <li value="2">Overview
                  <ol>
                    <li value="5"><dfn>Searching for Services</dfn>
                      <ol>
                        <li value="1"><dfn>UUID</dfn>
                          (defines <dfn>UUID alias</dfn>es and
                          the algorithm to compute <dfn>the 128-bit UUID represented</dfn>
                          by a UUID alias)
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
              </ol>
            </li>
            <li value="3">Generic Access Profile
              <ol>
                <li value="2">Profile Overview
                  <ol>
                    <li value="2">Profile Roles
                      <ol>
                        <li value="2">Roles when Operating over an LE Physical Transport
                          <ol>
                            <li value="1"><dfn>Broadcaster</dfn> Role</li>
                            <li value="2"><dfn>Observer</dfn> Role</li>
                            <li value="3"><dfn>Peripheral</dfn> Role</li>
                            <li value="4"><dfn>Central</dfn> Role</li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="3">User Interface Aspects
                  <ol>
                    <li value="2">Representation of Bluetooth Parameters
                      <ol>
                        <li value="2"><dfn>Bluetooth Device Name</dfn> (the user-friendly name)
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="6">Idle Mode Procedures &mdash; BR/EDR Physical Transport
                  <ol>
                    <li value="4"><dfn>Device Discovery Procedure</dfn></li>
                    <li value="5"><dfn>BR/EDR Bonding Procedure</dfn></li>
                  </ol>
                </li>
                <li value="9">Operational Modes and Procedures &mdash; LE Physical Transport
                  <ol>
                    <li value="1">Broadcast Mode and Observation Procedure
                      <ol>
                        <li value="2"><dfn>Observation Procedure</dfn></li>
                      </ol>
                    </li>
                    <li value="2">Discovery Modes and Procedures
                      <ol>
                        <li value="6"><dfn>General Discovery Procedure</dfn></li>
                        <li value="7"><dfn>Name Discovery Procedure</dfn></li>
                      </ol>
                    </li>
                    <li value="3"><dfn>Connection Modes and Procedures</dfn></li>
                    <li value="4">Bonding Modes and Procedures
                      <ol>
                        <li value="4"><dfn>LE Bonding Procedure</dfn></li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="10">Security Aspects &mdash; LE Physical Transport
                  <ol>
                    <li value="7"><dfn>Privacy Feature</dfn></li>
                    <li value="8">Random Device Address
                      <ol>
                        <li value="1"><dfn>Static Address</dfn></li>
                        <li value="2"><dfn>Private address</dfn>
                          <ol>
                            <li value="3">
                              <dfn>Resolvable Private Address Resolution Procedure</dfn>
                            </li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="11"><dfn>Advertising Data</dfn> and Scan Response Data Format
                  (defines <dfn>AD structure</dfn>)
                </li>
                <li value="15">Bluetooth Device Requirements
                  <ol>
                    <li value="1">Bluetooth Device Address (defines <dfn>BD_ADDR</dfn>)
                      <ol>
                        <li value="1">Bluetooth Device Address Types
                          <ol>
                            <li value="1"><dfn>Public Bluetooth Address</dfn></li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="16">Definitions
                  (defines <dfn>bond</dfn>)
                </li>
              </ol>
            </li>
            <li value="6">Attribute Protocol (ATT)
              <ol>
                <li value="3">Protocol Requirements
                  <ol>
                    <li value="2">Basic Concepts
                      <ol>
                        <li value="1"><dfn>Attribute Type</dfn></li>
                        <li value="2"><dfn>Attribute Handle</dfn></li>
                        <li value="9"><dfn>Long Attribute Values</dfn></li>
                      </ol>
                    </li>
                    <li value="4">Attribute Protocol Pdus
                      <ol>
                        <li value="1">Error Handling
                          <ol>
                            <li value="1"><dfn>Error Response</dfn></li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
              </ol>
            </li>
            <li value="7"><dfn lt="Generic Attribute Profile|GATT">Generic Attribute Profile</dfn> (GATT)
              <ol>
                <li value="2">Profile Overview
                  <ol>
                    <li value="2">Configurations and Roles
                      (defines <dfn>GATT Client</dfn> and <dfn>GATT Server</dfn>)
                    </li>
                    <li value="4">
                      <dfn>Profile Fundamentals</dfn>,
                      defines the <dfn>ATT Bearer</dfn>
                    </li>
                    <li value="5">Attribute Protocol
                      <ol>
                        <li value="2"><dfn>Attribute Caching</dfn></li>
                      </ol>
                    </li>
                    <li value="6"><dfn>GATT Profile Hierarchy</dfn>
                      <ol>
                        <li value="2"><dfn>Service</dfn></li>
                        <li value="3"><dfn>Included Service</dfn>s</li>
                        <li value="4"><dfn>Characteristic</dfn></li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="3"><dfn>Service Interoperability Requirements</dfn>
                  <ol>
                    <li value="1"><dfn>Service Definition</dfn></li>
                    <li value="3">Characteristic Definition
                      <ol>
                        <li value="1">Characteristic Declaration
                          <ol>
                            <li value="1"><dfn>Characteristic Properties</dfn></li>
                          </ol>
                        </li>
                        <li value="3">Characteristic <dfn>Descriptor</dfn> Declarations
                          <ol>
                            <li value="1"><dfn>Characteristic Extended Properties</dfn></li>
                            <li value="3"><dfn>Client Characteristic Configuration</dfn></li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="4">GATT Feature Requirements &mdash; defines the
                  <dfn lt="GATT procedure|GATT procedures">GATT procedures</dfn>.
                  <ol>
                    <li value="3">Server Configuration
                      <ol>
                        <li value="1"><dfn>Exchange MTU</dfn></li>
                      </ol>
                    </li>
                    <li value="4"><dfn>Primary Service Discovery</dfn>
                      <ol>
                        <li value="1"><dfn>Discover All Primary Services</dfn></li>
                        <li value="2"><dfn>Discover Primary Service by Service UUID</dfn></li>
                      </ol>
                    </li>
                    <li value="5"><dfn>Relationship Discovery</dfn>
                      <ol>
                        <li value="1"><dfn>Find Included Services</dfn></li>
                      </ol>
                    </li>
                    <li value="6"><dfn>Characteristic Discovery</dfn>
                      <ol>
                        <li value="1"><dfn>Discover All Characteristics of a Service</dfn></li>
                        <li value="2"><dfn>Discover Characteristics by UUID</dfn></li>
                      </ol>
                    </li>
                    <li value="7"><dfn>Characteristic Descriptor Discovery</dfn>
                      <ol>
                        <li value="1"><dfn>Discover All Characteristic Descriptors</dfn></li>
                      </ol>
                    </li>
                    <li value="8"><dfn>Characteristic Value Read</dfn></li>
                    <li value="9"><dfn>Characteristic Value Write</dfn></li>
                    <li value="10"><dfn>Characteristic Value Notification</dfn></li>
                    <li value="11"><dfn>Characteristic Value Indications</dfn></li>
                    <li value="12"><dfn>Characteristic Descriptors</dfn>
                      <ol>
                        <li value="1"><dfn>Read Characteristic Descriptors</dfn></li>
                        <li value="2"><dfn>Read Long Characteristic Descriptors</dfn></li>
                        <li value="3"><dfn>Write Characteristic Descriptors</dfn></li>
                        <li value="4"><dfn>Write Long Characteristic Descriptors</dfn></li>
                      </ol>
                    </li>
                    <li value="14"><dfn local-lt="procedure times out">Procedure Timeouts</dfn></li>
                  </ol>
                </li>
                <li value="6"><dfn>GAP Interoperability Requirements</dfn>
                  <ol>
                    <li value="1">BR/EDR GAP Interoperability Requirements
                      <ol>
                        <li value="1">Connection Establishment</li>
                      </ol>
                    </li>
                    <li value="2">LE GAP Interoperability Requirements
                      <ol>
                        <li value="1">Connection Establishment</li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="7">Defined Generic Attribute Profile Service
                  <ol>
                    <li value="1"><dfn>Service Changed</dfn></li>
                  </ol>
                </li>
              </ol>
            </li>
            <li value="8">Security Manager Specification
              <ol>
                <li value="2">Security Manager
                  <ol>
                    <li value="4">Security in Bluetooth Low Energy
                      <ol>
                        <li value="1"><dfn>Definition of Keys and Values</dfn>,
                          defines the
                          <dfn lt="Identity Resolving Key|IRK">Identity Resolving Key</dfn>
                          (<abbr title="Identity Resolving Key">IRK</abbr>)
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
              </ol>
            </li>
          </ol>
        </li>
        <li value="6">Core System Package [Low Energy Controller volume]
          <ol type="A">
            <li value="2">Link Layer Specification
              <ol>
                <li value="1">General Description
                  <ol>
                    <li value="3">Device Address
                      <ol>
                        <li value="1"><dfn>Public Device Address</dfn></li>
                        <li value="2">Random Device Address
                          <ol>
                            <li value="1"><dfn>Static Device Address</dfn></li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
                <li value="4">Air Interface Protocol
                  <ol>
                    <li value="4">Non-Connected States
                      <ol>
                        <li value="3">Scanning State
                          <ol>
                            <li value="1"><dfn>Passive Scanning</dfn></li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                  </ol>
                </li>
              </ol>
            </li>
          </ol>
        </li>
      </ol>
    </dd>
    <dt>[[!BLUETOOTH-SUPPLEMENT6]]</dt>
    <dd>
      <ol type="A">
        <li value="1">Data Types Specification
          <ol>
            <li value="1">Data Types Definitions and Formats
              <!-- The section names here are really general, so I've added
                   "Data Type" to some. -->
              <ol>
                <li value="1"><dfn local-lt="Service UUIDs">Service UUID Data Type</dfn></li>
                <li value="2"><dfn>Local Name Data Type</dfn></li>
                <li value="3"><dfn>Flags Data Type</dfn>
                  (defines the <dfn>Discoverable Mode</dfn> flags)</li>
                <li value="4"><dfn>Manufacturer Specific Data</dfn></li>
                <li value="5"><dfn>TX Power Level</dfn></li>
                <li value="11"><dfn>Service Data</dfn></li>
                <li value="12"><dfn>Appearance</dfn></li>
              </ol>
            </li>
          </ol>
        </li>
      </ol>
    </dd>
  </dl>
</section>
